分配到位域时的GCC转换警告

Mof*_*ofX 8 c gcc compiler-warnings suppress-warnings

有没有办法在这段代码中抑制gcc生成的警告:

int main() {
    struct flagstruct {
        unsigned flag : 1;
    } a,b,c;

    a.flag = b.flag | c.flag;

    return a.flag;
}
Run Code Online (Sandbox Code Playgroud)

警告是

warning: conversion to 'unsigned char:1' from 'int' may alter its value [-Wconversion]
Run Code Online (Sandbox Code Playgroud)

看起来两个标志在组合在一起时扩展为int.我认为真的很奇怪的是,将两个标志中的任何一个投射到无符号都会发出警告.

a.flag = (unsigned)b.flag | c.flag;
Run Code Online (Sandbox Code Playgroud)

这是一个编译器错误还是它应该以这种方式工作?

Pas*_*uoq 7

看起来两个标志在组合在一起时扩展为int.

这是整数提升,它在C99标准的奇怪措辞条款6.3.1.1:2中定义:

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

...

- _Bool,int,signed int或unsigned int类型的位字段.如果int可以表示原始类型的所有值,则该值将转换为int; 否则,它将转换为unsigned int.这些被称为整数促销.整数促销不会更改所有其他类型.

首先,处理器不直接对位字段计算,也可以不具有指令来计算上更窄的整数类型charshort.C标准通过仅具有上定义的算术运算捕获此int,unsigned int和更宽的整数类型.在上面标准上写着"可以使用"的情况下,它正在尝试(很差)表示所有短类型和位字段必须在参与算术时intunsigned int之前被提升.

其次,所有无符号位字段是不够宽,包括为无法表示的值int被提升至int.换句话说,GCC通过将您的无符号位字段提升为已签名的行为来表现int,并且像您一样添加显式强制转换似乎是未来(以及针对警告)的最佳策略.

我认为真的很奇怪的是,将两个标志中的任何一个投射到无符号都会发出警告.

通常的算术转换是C标准中另一个有趣的概念(C99中的6.3.1.8),结果是如果两个操作数中的任何一个被显式转换为a unsigned int,那么另一个操作数也会隐式地转换为unsigned int|操作是unsigned int产生unsigned int结果的操作.

换句话说,(unsigned)b.flag | c.flag严格相当于(unsigned)b.flag | (unsigned)c.flag.在这种情况下,编译器认为没有理由警告分配,因为计算的结果是a unsigned int.

  • 我刚刚尝试了http://gcc.godbolt.org/上提供的所有编译器.除了clang> = 3.2之外,他们中的大多数都会发出警告.clang-3.0确实产生了这个警告.因此clang似乎同意我的意见,它实际上是一个编译器错误.最奇怪的是所有可用的gcc/g ++版本生成的消息:`warning:从'unsigned int'转换为'unsigned char:1'可能会改变它的值[-Wconversion]`.将转换为unsigned添加到第一个标志会删除警告.将unsigend转换为unsigned会删除警告?! (2认同)

Mof*_*ofX 2

一年后我修改了这个问题:

刚刚用不同的编译器版本再次测试。当我第一次遇到这个错误时(现在我很确定,我可以称它为错误),我已经意识到该警告仅存在于 clang < 3.1 和当时的所有 GCC 版本中。所有低于 5 的 GCC 版本仍然会产生该警告。

由于没有其他方法来消除错误,唯一的解决方案是更新到 GCC > 5 或添加 unsigned cast a.flag = (unsigned)b.flag | c.flag;