我正在完成“以艰难的方式学习C”一书中的练习。练习7要求读者找到使unsigned long
的范围超过的值。
将
long
更改为unsigned long
并尝试找到使其太大的数字。
所以我的方法是首先在我的机器上获得unsigned long
的大小:
printf("SIZEOF ULONG: %lu", sizeof(unsigned long));
这会打印出8
。所以假设unsigned long
在我的机器上占用了64位,我查找了Wikipedia的最大范围。
64位(字,双字,长字,长字,四字,四字,qword,int64)
我期待声明具有上述值的unsigned long
将在没有警告的情况下编译,直到我将值增加1.但结果是不同的。编译以下程序会导致警告。
#include <stdio.h>
int main()
{
unsigned long value = 18446744073709551615;
printf("SIZEOF ULONG: %lu", sizeof(unsigned long));
printf("VALUE: %lu", value);
return 0;
}
bla.c: In function ‘main’:
bla.c:5:27: warning: integer constant is so large that it is unsigned
unsigned long value = 18446744073709551615;
^~~~~~~~~~~~~~~~~~~~
那么为什么gcc抱怨价值变大,我以为我已经宣布它为unsigned
?
如果十进制整数常量适合该范围,则它们的类型为int
,否则它们的类型为long
或long long
。它们没有无符号类型,如果值超出了那些有符号范围,则会收到警告。您需要为常量添加ul
后缀以使其具有正确的类型。
在不知道其大小的情况下,还可以更轻松地获得此类型的最大值。只需将-1转换为此类型。
unsigned long value = (unsigned long)-1;
对于值的整数文字,你需要一个后缀,而不是long int(或者long long int,因为C99和C ++ 11)。以下任何一项都符合unsigned long int:
unsigned long value = 18446744073709551615u;
unsigned long value = 18446744073709551615lu;
unsigned long value = 18446744073709551615ul;
请在此处查看后缀表:
https://en.cppreference.com/w/c/language/integer_constant(对于C)https://en.cppreference.com/w/cpp/language/integer_literal(对于C ++)
编译器分几步处理unsigned long value = 18446744073709551615;
。在用值初始化value
之前,它必须从源代码中读取18446744073709551615
并解释它。
源代码中的数字18446744073709551615
独立存在 - 它不会立即受到这样一个事实的影响:在一瞬间,它将用于初始化value
。它根据C标准中的规则进行处理。
这些规则说带有十进制数字且没有后缀的数字是int
,long int
或long long int
,无论哪个是第一个可以代表该值的数字。由于18446744073709551615是如此之大,它不适合任何这些类型。
编译器警告您,由于18446744073709551615不适合任何这些类型,因此它使用无符号类型。在其他情况下,这可能会改变代码的含义。但是,在这种情况下,由于该值立即用于初始化unsigned long
,因此它将具有所需的效果。
要解决此问题,您可以添加u
后缀,将其更改为18446744073709551615u
。对于以u
为后缀的十进制数字,C标准表示该类型是可以表示该值的unsigned int
,unsigned long int
或unsigned long long int
中的第一个。
(C标准继续说,如果一个值对于列出的类型来说太大,C实现可以用扩展的整数类型表示它或者它没有类型。没有类型的后果可能是有趣的探索,但这是语言律师问题的主题。)
使用unsigned long value = 18446744073709551615ul;
其他18446744073709551615ul
读作int而不是long
如果你想知道无符号长整数和最大值的位数:
#include <stdio.h>
#include <limits.h>
int main()
{
printf("number of bits in ULONG: %d\nULONG_MAX = %lu\n",
sizeof(unsigned long) * CHAR_BIT,
ULONG_MAX);
return 0;
}
编译和执行:
pi@raspberrypi:/tmp $ gcc -pedantic -Wextra u.c
pi@raspberrypi:/tmp $ ./a.out
number of bits in ULONG: 32
ULONG_MAX = 4294967295
是的,我的长期不在64位
整数常数18446744073709551615
太大,无法在系统上表示为int
,long int
或long long int
。编译器警告你它使它成为unsigned long long
的事实。
让我们尝试编译这个程序:
#include <stdio.h>
int main() {
if (-1 < 18446744073709551615)
printf("TRUE\n");
else
printf("FALSE\n");
return 0;
}
gcc
确实发出警告:
#1 with x86-64 gcc 8.2
<source>: In function 'main':
<source>:7:14: warning: integer constant is so large that it is unsigned
if (-1 < 18446744073709551615)
^~~~~~~~~~~~~~~~~~~~
程序输出是这样的:
TRUE
clang
生成更明确的警告:
#1 with x86-64 clang 7.0.0
<source>:7:14: warning: integer literal is too large to be represented in a signed integer type, interpreting as unsigned [-Wimplicitly-unsigned-literal]
if (-1 < 18446744073709551615)
^
但程序输出是:
FALSE
如果18446744073709551615
确实被解释为无符号,就像写成18446744073709551615u
或0xffffffffffffffff
一样,必须使用无符号算术进行比较并且失败,因为两个数字具有相同的值。 clang
做它所说的,但gcc
没有。
在您的情况下,将值存储到unsigned long
变量应该产生预期的结果,但是向常量添加u
后缀将确保编译器使用正确的类型对其进行解析。
但请注意,您可以通过将-1
强制转换为该类型来获取任何无符号类型的最大值。您还可以使用<limits.h>
中定义的宏:类型unsigned long
的最大值是ULONG_MAX
。