为什么GCC在将无符号长度设置为2 ^ 64-1时会发出警告?

时间:2017-11-09 16:02:07

标签: c gcc

C标准规定long int至少为4个字节 - 在我的系统上它是8个字节。

这意味着我可以在long中存储最多2 ^ 63 -1的值,在unsigned long中存储2 64 -1。

但是,当使用-Wall标记编译以下代码时,它会发出警告[Wimplicitly-unsigned-literal]

int main (int argc, char ** argv) {

  unsigned long a;
  a = 18446744073709551615; // 2^64-1

}

如果我使用2 63 -1(9223372036854775807),它会编译而没有警告(正如预期的那样 - 2 63 -1将适合{{1} })。

对于项目,我需要在signed long int中获得最大值,并且我发现unsigned long不会引发此警告。我的老师然后建议我可以使用(9223372036854775807 << 1) + 1中定义的ULONG_MAX,但这没有给出任何警告。

为什么我不能在没有警告声明它被隐式转换的情况下执行此操作 - 当我明确声明它时?

3 个答案:

答案 0 :(得分:5)

根据C标准,没有后缀的十进制常量的类型是intlong intlong long int,特别是足以表示该值的第一个。在你的C实现中,没有一个可以代表18446744073709551615,因为它太大了。

为了适应您,编译器正在进行类型unsigned long。从技术上讲,这不符合C标准,因此编译器会警告您。

在这种情况下,不会造成任何伤害,因为您要将值分配给unsigned long。但是在某些情况下使用错误的类型会导致问题,因此通常应该为这些常量添加后缀,以确保它们与预期的使用方式相匹配。在这种情况下,u就足够了;与非混合类型一样,编译器将决定是使用unsigned intunsigned long int还是unsigned long long int,具体取决于数量的大小和类型的功能。

答案 1 :(得分:4)

你明确地声明了它,但没有U,这会使它无符号。由于没有带有此值的带符号整数常量,因此它隐式地使其无符号,为您提供最好使其明确的信息。

使用a = 18446744073709551615U;执行此操作。

答案 2 :(得分:3)

查看此fancy table from the standard (6.4.4.1p5)

integer constant promotion table from the standard

解释了整数文字如何适合整数类型。

基本上,因为你的文字是十进制的并且没有后缀,所以它会尝试适合以下类型:

int
long int
long long int

由于它不符合long long int,因此您会收到警告。

如果您添加Uu)后缀(或ULUL或小写变体):

unsigned long a = 18446744073709551615U;

您不会遇到这个问题,因为您将转移到促销序列:

unsigned int
unsigned long int
unsigned long long int

(或ULUL的相应子集)和文字将适合 在unsigned long int(如果它确实是第一个无符号类型,通常不少于64个可用位)。

或者,您可以通过切换到八进制或十六进制文字来关闭警告:

unsigned long a = 0xFFFFFFFFFFFFFFFF;
unsigned long octal_a = 01777777777777777777777;

这些推广顺序是:

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

根据链接表,它再次允许它们适合第一个64位大的无符号类型(通常为unsigned long int)。

你绝对应该做的是:

(9223372036854775807 << 1) + 1 //don't do this!

因为无论是否生成警告,都会通过中断6.5.7p4来调用未定义的行为。