C中的按位“非”运算符返回签名结果

时间:2013-05-11 11:41:24

标签: c linux gcc gnu99

考虑以下代码:

uint16_t a = ~ ( uint16_t ) 0;
int16_t  b = ~ ( int16_t ) 0;
printf (
    "%d %d %d %d\n",
    a  == ~ ( uint16_t ) 0,
    a  == ( uint16_t ) ( ~ ( uint16_t ) 0 ),
    b  == ~ ( int16_t ) 0,
    b  == ( int16_t ) ( ~ ( int16_t ) 0 )
);

输出结果为:

  

0 1 1 1

GCC发出关于a == ~ ( uint16_t ) 0的警告:

  由于数据类型[-Wtype-limits]

的范围有限,

比较始终为假

为什么按位“not”运算符尝试返回有符号值?我该如何防止这种情况?

2 个答案:

答案 0 :(得分:9)

  

为什么按位“not”运算符试图返回有符号值?

因为没有运算符适用于小于int的类型;当用作操作数时,较小的类型(如果uint16_t超过16位,则包括int提升。如果原始类型的所有值都由int表示,就像它们在这里一样,那么促销将是int

  

我该如何防止这种情况?

你不能;这就是语言的运作方式。您必须将运算符的结果转换为所需的类型,或者像初始化a时一样隐式,或者使用强制转换显式转换。请注意,在应用~之前,您不需要强制转换;但为了获得最大的可移植性,在执行按位逻辑时应该坚持使用无符号类型:

uint16_t a = ~0u;
printf("%d\n", a == (uint16_t)~0u);  // Should print 1

答案 1 :(得分:3)

Sign是我们在位模式之上的概念。逐位不(〜)仅涉及位模式而不涉及值的符号。注明无符号值的结果是相同的。

说过看C标准:http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf(草稿版免费提供)。第6.3.1.4节,第51页:

  

如果int可以表示原始类型的所有值(受宽度限制,对于位域),该值将转换为int;否则,它将转换为unsigned int。这些被称为整数促销。 (58)所有其他类型的整数促销不变。

我认为这意味着,当我们实际操作它们时,char和short类型将被提升为int(或unsigned int,具体取决于大小)。这是有道理的,因为我们希望尽可能快地完成操作,因此我们应该以机器的原始大小为目标。

鉴于此,我们可以看到实际发生的事情。机器将以'int'大小执行所有操作,因为'=='和'〜'的操作数都可以放在int字段中,我假设你的机器是32位。

现在首先要看的是'a'的价值。我们取0,不是它,得到0xFFFFFFFF。我们将其赋值给uint16_t值并得到0xFFFF。当我们准备进行比较时,我们加载0xFFFF,实现值无符号并将零扩展为0x0000FFFF。对于'b'的值,一切都是相同的,除非我们读取0xFFFF进行比较时我们将其扩展为0xFFFFFFFF。现在为您的案例:

  1. 注意零给出0xFFFFFFFF,与0x0000FFFF相比将失败。
  2. 我们取了0xFFFFFFFF,将其切换为0xFFFF然后将其扩展为0x0000FFFF,给出与'a'相同的值。
  3. 等等。