为什么GCC在将有符号的字面量分配给无符号类型时不会发出警告?

17

在该网站上有许多问题揭示了混合使用有符号和无符号类型时的陷阱,大多数编译器似乎能够很好地生成此类警告。 然而,GCC在将有符号常量分配给无符号类型时似乎并不关心!考虑以下程序:

/* foo.c */
#include <stdio.h>
int main(void)
{
    unsigned int x=20, y=-30;
    if (x > y) {
        printf("%d > %d\n", x, y);
    } else {
        printf("%d <= %d\n", x, y);
    }
    return 0;
}

使用GCC 4.2.1编译以下代码,在控制台上没有输出:

gcc -Werror -Wall -Wextra -pedantic foo.c -o foo

生成的可执行文件会产生以下输出:

$ ./foo
20 <= -30

为什么 GCC 将有符号值 -30 分配给无符号整数变量 y 时,不会生成任何警告或错误消息?


3
请注意,在打印“unsigned int”时应使用%u。 - Bastien Léonard
@Bastien,确实;更有趣的是,正如下面AndreyT在他的答案中指出的那样,printf("%d")显示的是有符号值,而不是比较运算符使用的无符号值(4294967266)! - maerics
1
这是因为带符号整数使用的是二进制补码表示法: http://en.wikipedia.org/wiki/Two%27s_complement (虽然这种表示法不被 C 标准保证,但在实践中我从未听说过其他表示法仍在使用)。 - Bastien Léonard
3个回答

23

使用-Wconversion选项:

~/src> gcc -Wconversion -Werror -Wall -Wextra -pedantic -o signwarn signwarn.c
cc1: warnings being treated as errors
signwarn.c: In function 'main':
signwarn.c:5: error: negative integer implicitly converted to unsigned type

我想这里的问题是gcc实际上在生成警告方面做得非常好,但默认情况下不会为(有时是意外的)情况生成警告。建议浏览可用的警告并选择一组选项来生成您认为有帮助的警告。或者选择所有选项,优化代码直到它发光!:)


1
太好了,谢谢!这让我希望有一个 Wallall 存在 ;) - niCk cAMel
1
请注意,在C++中,-Wsign-conversion不是默认启用的,因此最好始终明确添加它。 - yugr

9

将负数值转换为无符号类型是C语言的一项功能。因此,默认情况下不会发出警告。如果您需要,可以明确请求警告。

至于您的程序输出的内容……使用printf%d格式说明符和超出int类型范围的无符号值会导致未定义行为,这就是您在实验中真正观察到的结果。


еШњпЉМеЊИе•љзЪДеПСзО∞гАВprintf("%d", y);дЄОy=-30;еЃМеЕ®дЄНеРМгАВ - Pascal Cuoq
很好的一点,我可以想象使用负值来创建所需的位字段,例如。 - maerics
1
是的 - 将负数转换为无符号数被定义为产生一个“模”无符号整数大小的结果,因此(unsigned)-1是一种完全合适的方式来表示“一个所有位都是1的无符号数”。当使用2的补码表示有符号数时,这种“转换”只是与有符号数相同的位模式。 - greggo

2

使用 (unsigned)-1 是一种常用的设置所有位的方法,有时甚至被引用为 C 语言中这个(误)特性的原因,即使是那些应该更清楚的人也会这样说。它既不明显也不可移植 - 你想要使用的表达式来设置所有位是 ~0。


3
我认为"uint32_t x=-1;"的含义很明显,标准规定了确切的行为,而这种方式比"uint32_t x=0;"或"uint32_t x=0u;"更具可移植性。在使用补码系统时,"0"形式会将"x"设置为0xFFFF8001ul;在任何16位系统上,"0u"形式会将"x"设置为0x0000FFFFul;而使用"-1"的形式将适用于定义了"uint32_t"的任何系统。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接