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

时间:2014-08-25 06:23:05

标签: 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;
}

警告是

warning: conversion to 'unsigned char:1' from 'int' may alter its value [-Wconversion]

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

a.flag = (unsigned)b.flag | c.flag;

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

3 个答案:

答案 0 :(得分:7)

  

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

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

  

无论是int还是unsigned,都可以在表达式中使用以下内容   可以使用int:

     

...

     

- _Bool,int,signed int或unsigned int类型的位字段。如果   int可以表示原始类型的所有值,值为   转换为int;否则,它将转换为unsigned int。   这些被称为整数促销。所有其他类型都保持不变   通过整数促销。

首先,处理器不直接计算位字段,也可能没有指令来计算较窄的整数类型charshort。 C标准通过仅在intunsigned int和更宽的整数类型上定义算术运算来捕获这一点。如果标准上面写着“可能会被使用”,那么它(很难)表示所有短类型和位字段必须提升为intunsigned int之前参加算术。

其次,所有不足以包含无法表示为int的值的无符号位字段将提升为int。 换句话说,GCC通过将您的无符号位字段提升为带符号的int来表现符合标准,并且像您一样添加显式强制转换似乎是未来(以及针对不良意外)的最佳策略警告)。

  

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

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

换句话说,(unsigned)b.flag | c.flag严格等同于(unsigned)b.flag | (unsigned)c.flag。在这种情况下,编译器认为没有理由警告赋值,因为计算结果是unsigned int

答案 1 :(得分:0)

解决此警告的最佳方法是明确承认您不需要多余的位:

  

a.flag =(b.flag | c.flag)& 00000001;

我目前正在使用 arm-none-eabi-gcc.exe(适用于ARM嵌入式处理器的GNU工具)5.4.1 20160609(发布)[ARM / embedded-5-branch revision 237715] ,这是摆脱它们的唯一一致方法。

不能评论为什么显式转换为(unsigned)会在您发布的情况下解决此问题。充其量只是奇特,我怀疑你在其他情况下是否也会幸运。

答案 2 :(得分:0)

一年后,我修改了该问题:

只需使用不同的编译器版本进行再次测试。当我第一次遇到此错误(我不太确定,可以允许我将其称为错误)时,它已经意识到警告仅存在于clang <3.1和当时的所有GCC版本中。该警告仍由所有GCC版本<5产生。

由于没有其他方法可以使错误静音,因此唯一的解决方案是将GCC> 5更新或添加未签名的强制转换a.flag = (unsigned)b.flag | c.flag;