无符号整数位域移位产生有符号整数

时间:2014-03-03 16:58:24

标签: c gcc clang bit-shift bit-fields

考虑以下计划test.c

#include <stdio.h>

struct test {
    unsigned int a:5;
};

int main () {
    unsigned int i;
    struct test t = {1};
    for (i = 0; i < t.a << 1; i++)
        printf("%u\n", i);
    return 0;
}

使用gcc -Wsign-compare test.c编译时,会生成以下警告(使用gcc 4.8.1进行测试):

test.c:9:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
     for (i = 0; i < t.a << 1; i++)
                   ^

clang -Wsign-compare test.c生成以下内容(使用clang 3.2进行测试):

test.c:9:19: warning: comparison of integers of different signs: 'unsigned int' and 'int' [-Wsign-compare]
    for (i = 0; i < t.a << 1; i++)
                ~ ^ ~~~~~~~~
1 warning generated.

因此,右操作数(移位的无符号位字段)变为有符号的int。此警告显示包含的1到31之间的任何位字段值。对于更高的值,不会产生警告。这很奇怪。

使用类型unsigned shortunsigned intunsigned long的位字段对此进行了测试。后者不会显示包含在32和64之间的位字段值的任何警告。

当没有进行移位时没有警告,因此位字段按预期无符号。

为什么大小低于32位的位字段在移位时会被签名?我认为这不是错误,因为这与gccclang一致。我一定错过了一些关于比特字段(或移位)如何工作的信息但是什么?如何转换无符号值会产生有符号值?

1 个答案:

答案 0 :(得分:7)

整数促销适用于draft C99 standard部分6.5.7 按位移位运算符段落 3 中涵盖的转换操作数,其中说明了(强调我的前进):

  

对每个操作数执行整数提升。[...]

和位字段的整数提升包含在6.3.1.1 布尔,字符和整数 2 中,其中包含:

  

在int或unsigned int可以的任何地方都可以使用以下代码   使用:

并包含以下项目符号:

  

- _Bool,int,signed int或unsigned int类型的位字段

然后说:

  

如果int可以表示原始类型的所有值,则该值将转换为int; 否则,它将转换为unsigned int。 这些称为整数   促销活动 48)所有其他类型的整数促销活动均未更改。

draft C11标准中澄清:

  

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

所以这是预期的行为。