代码段:
long rangeVar = 0; rangeVar = atol(p_value); if (rangeVar >= -2147483648 && rangeVar <= 2147483647)
编译时,我得到:警告:此十进制常量仅在ISO C90中无符号提前致谢
bn31dyow1#
十进制整数常量类型的规则在ISO C标准的1990和1999版本之间发生了变化。在1990年的版本中,无后缀十进制整型常量的类型是int、long int或unsigned long int中的第一个,它的值可以用这些类型表示(C90没有long long或unsigned long long类型)。在1999和2011版本中,其类型为int、long int、long long int中的一种;它从来都不是无符号类型。特定常量的类型(例如2147483648)将根据您使用的编译器的整数类型范围而变化。如果您的编译器的long类型恰好是32位,那么2147483648将是unsigned long类型(如果您的编译器使用C90规则),如果使用C11规则,则为long long类型(long long保证至少为64位)。编译器会就此发出警告。你可以添加后缀来指定常量的类型--但是对于纯符号的int没有后缀,你可以为unsigned int添加U,为long添加L,为无符号的long添加UL,等等。记住-2147483648 * 不是 * 整型常量是很重要的;而2147483648本身是整型常量,并且-2147483648是将一元减运算符应用于该常量的表达式。在C90规则下,如果常量是unsigned long类型,则这是 unsigned 一元减,其在无符号算术规则下产生值2147483648。在C99或C11规则下,2147483648很可能是类型(有符号)long long,并且对其求反产生-2147483648,其也是类型long long。您有时会看到使用(-2147483647 - 1)来避免此问题的代码;给定32位X1 M30 N1 X,X1 M31 N1 X是类型X1 M32 N1 X,并且表达式的结果产生预期的X1 M33 N1 X值而没有溢出。当然,如果您的编译器对整数类型有不同的大小,这可能会变得更加复杂。更新:当我写这篇文章的时候,gcc的默认方言是-std=gnu89,从那以后,它被改成了-std=gnu11(或者在未发布的版本中是-std=gnu17),我不确定这对警告有什么影响。
int
long int
unsigned long int
long long
unsigned long long
long long int
2147483648
long
unsigned long
unsigned int
U
L
UL
-2147483648
(-2147483647 - 1)
-std=gnu89
-std=gnu11
-std=gnu17
ojsjcaue2#
是的,这是编译器没有很好处理的一件事。问题是在编译过程中,这是一个被取反的数字2147483648,2147483648超出了整数的范围。即使-2147483648也不会!无论如何,要消除警告,可以通过写入-2147483648LL将常量转换为64位数。不过这有点过分了,所以首选的方法是使用INT_MIN作为常量,但是这样就需要包含<limits.h>。
-2147483648LL
INT_MIN
<limits.h>
pgky5nke3#
是的,2147483648不是一个有效的正值,因为它超出了32位机器上2的补码的范围,所以他们只是想警告你,在一些编译器上,如果他们不以现代的方式处理求反,这可能不会给予你想要的值。我觉得有必要补充另一个答案,指出如果您查看大多数limits. h实现,您会发现它们使用(-2147483647 - 1)解决了这个问题。
3条答案
按热度按时间bn31dyow1#
十进制整数常量类型的规则在ISO C标准的1990和1999版本之间发生了变化。
在1990年的版本中,无后缀十进制整型常量的类型是
int
、long int
或unsigned long int
中的第一个,它的值可以用这些类型表示(C90没有long long
或unsigned long long
类型)。在1999和2011版本中,其类型为
int
、long int
、long long int
中的一种;它从来都不是无符号类型。特定常量的类型(例如
2147483648
)将根据您使用的编译器的整数类型范围而变化。如果您的编译器的long
类型恰好是32位,那么2147483648
将是unsigned long
类型(如果您的编译器使用C90规则),如果使用C11规则,则为long long
类型(long long
保证至少为64位)。编译器会就此发出警告。你可以添加后缀来指定常量的类型--但是对于纯符号的
int
没有后缀,你可以为unsigned int
添加U
,为long
添加L
,为无符号的long添加UL
,等等。记住
-2147483648
* 不是 * 整型常量是很重要的;而2147483648
本身是整型常量,并且-2147483648
是将一元减运算符应用于该常量的表达式。在C90规则下,如果常量是unsigned long
类型,则这是 unsigned 一元减,其在无符号算术规则下产生值2147483648
。在C99或C11规则下,2147483648
很可能是类型(有符号)long long
,并且对其求反产生-2147483648
,其也是类型long long
。您有时会看到使用
(-2147483647 - 1)
来避免此问题的代码;给定32位X1 M30 N1 X,X1 M31 N1 X是类型X1 M32 N1 X,并且表达式的结果产生预期的X1 M33 N1 X值而没有溢出。当然,如果您的编译器对整数类型有不同的大小,这可能会变得更加复杂。
更新:当我写这篇文章的时候,gcc的默认方言是
-std=gnu89
,从那以后,它被改成了-std=gnu11
(或者在未发布的版本中是-std=gnu17
),我不确定这对警告有什么影响。ojsjcaue2#
是的,这是编译器没有很好处理的一件事。问题是在编译过程中,这是一个被取反的数字2147483648,2147483648超出了整数的范围。即使-2147483648也不会!
无论如何,要消除警告,可以通过写入
-2147483648LL
将常量转换为64位数。不过这有点过分了,所以首选的方法是使用
INT_MIN
作为常量,但是这样就需要包含<limits.h>
。pgky5nke3#
是的,2147483648不是一个有效的正值,因为它超出了32位机器上2的补码的范围,所以他们只是想警告你,在一些编译器上,如果他们不以现代的方式处理求反,这可能不会给予你想要的值。
我觉得有必要补充另一个答案,指出如果您查看大多数limits. h实现,您会发现它们使用
(-2147483647 - 1)
解决了这个问题。