首页 文章

为什么最小的int,-2147483648具有类型'long'? [重复]

提问于
浏览
163

这个问题在这里已有答案:

对于学校项目,我要编写C函数printf . 事情进展顺利,但有一个问题我找不到合适的答案,所以我在这里 .

printf("PRINTF(d) \t: %d\n", -2147483648);

告诉我( gcc -Werror -Wextra -Wall ):

error: format specifies type 'int' but the argument has type 'long'
      [-Werror,-Wformat]
        printf("PRINTF(d) \t: %d\n", -2147483648);
                              ~~     ^~~~~~~~~~~
                              %ld

但如果我使用int变量,一切进展顺利:

int i;

i = -2147483648;
printf("%d", i);

为什么?

编辑:

我理解了许多观点,而且非常有趣 . 无论如何,我猜 printf 正在使用 <stdarg.h> librairy,因此, va_arg(va_list ap, type) 也应该返回正确的类型 . 对于 %d%i ,显然返回的类型是 int . 它有什么改变吗?

2 回答

  • 59

    在C中, -2147483648 不是整数常量 . 2147483648 是一个整型常量, - 只是一个应用于它的一元运算符,产生一个常量表达式 . 2147483648 的值不适合 int (它太大, 2147483647 通常是最大的整数),因此整数常量的类型为 long ,这会导致您观察到的问题 . 如果您想提及 int 的下限,请使用 <limits.h> 中的宏 <limits.h> (便携式方法)或小心避免提及 2147483648

    printf("PRINTF(d) \t: %d\n", -1 - 2147483647);
    
  • 223

    问题是 -2147483648 不是整数文字 . 它是一个由一元否定运算符 - 和整数 2147483648 组成的表达式,如果 int 是32位,则它太大而不能成为 int . 由于编译器将在应用否定运算符之前选择适当大小的有符号整数来表示 2147483648 ,因此结果的类型将大于 int .

    如果您知道 int 是32位,并且想要在不破坏可读性的情况下避免警告,请使用显式强制转换:

    printf("PRINTF(d) \t: %d\n", (int)(-2147483648));
    

    那个's defined behaviour on a 2'补充机器有32位 int s .

    为了提高理论上的可移植性,请使用 INT_MIN 而不是数字,并告诉我们您在哪里找到非二进制补码机器进行测试 .


    需要说明的是,最后一段部分是一个笑话 . 如果你的意思是“最小的 int ”, INT_MIN 肯定是要走的路,因为 int 的大小不一 . 例如,仍有许多16位实现 . 写出-231只有在你绝对总是指那个值时才有用,在这种情况下你可能会使用像 int32_t 这样的固定大小的类型而不是 int .

    你可能想要一些替代方法来写出十进制数字,以便让那些可能没有注意到 21474836482174483648 之间区别的人更清楚,但你需要小心 .

    如上所述,在32位二进制补码机器上, (int)(-2147483648) 不会溢出,因此定义明确,因为 -2147483648 将作为更宽的有符号类型进行处理 . 但是, (int)(-0x80000000) 也是如此 . 0x80000000 将被视为 unsigned int (因为它适合无符号表示); -0x80000000 是明确定义的(但如果 int 是32位,则 - 无效),并且生成的 unsigned int 0x80000000 转换为 int 会导致溢出 . 为避免溢出,您需要将十六进制常量转换为有符号类型: (int)(-(long long)(0x80000000)) .

    同样,如果要使用左移运算符,则需要注意 . 1<<31 在具有32位(或更小) int s的32位计算机上是未定义的行为;如果 int 至少为33位,它将仅计算为231,因为如果 k 严格小于左侧参数的整数类型的非符号位数,则左移 k 位只能很好地定义 .

    1LL<<31 是安全的,因为 long long int 需要能够代表263-1,所以它的位大小必须大于32.所以表格

    (int)(-(1LL<<31))
    

    可能是最具可读性的 . 因人而异 .


    对于任何通过的学习者,这个问题被标记为C,并且最新的C草案(n1570.pdf)表示,对于 E1 << E2 ,其中 E1 具有有符号类型,仅当 E1 为非负且 E1 × 2E2 "is representable in the result type"时才定义该值 . (§6.5.7第4段) .

    这与C不同,其中如果 E1 是非负的并且 E1 × 2E2 “可以在结果类型的相应无符号类型中表示”,则定义左移运算符的应用(§5.8第2段,强调添加) .

    在C中,根据最新的标准草案,如果值不能在目标类型中表示,则整数值到有符号整数类型的转换是实现定义的(第4.7节第3段) . C标准的相应段落 - §6.3.1.3段 . 3 - 说"either the result is implementation-defined or an implementation-defined signal is raised" . )

相关问题