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

boo*_*leg 12 c gcc bit-shift clang 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;
}
Run Code Online (Sandbox Code Playgroud)

编译时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++)
                   ^
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

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

这是用类型的字段测试的unsigned short,unsigned intunsigned long.后者不会显示包含32到64之间的位字段值的任何警告.

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

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

Sha*_*our 7

整数推广应用于草案C99标准部分所涵盖的转换操作数,6.5.7 按位移位操作符3段说明(强调我的前进):

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

并且第2段中的6.3.1.1 布尔,字符和整数部分涵盖了位字段的整数提升,其中说:

如果可以使用int或unsigned int,则可以在表达式中使用以下内容:

并包含以下项目符号:

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

然后说:

如果int可以表示原始类型的所有值,则该值将转换为int; 否则,它将转换为unsigned int.这些被称为整数促销.48)所有其他类型的整数提升不变.

C11标准草案中澄清:

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

所以这是预期的行为.

  • @bootleg如果能够或不能提升32位`unsigned int`取决于`unsigned int`的宽度.32和64位`unsigned`的结果不同. (2认同)