C语言 分配给位域时的 GCC 转换警告



有没有办法抑制 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;

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

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

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

以下内容可用于表达式中,只要是 int 或无符号 int 可用于:

— 类型为 _Bool、int、有符号 int 或无符号 int 的位字段。如果 int 可以表示原始类型的所有值,值为 转换为整数;否则,它将转换为无符号的 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

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

a.flag = (b.flag | c.flag( & 0x00000001;

我目前正在使用 arm-none-eabi-gcc.exe(GNU Tools for ARM Embedded Processor( 5.4.1 20160609 (release( [ARM/embedded-5-branch revision 237715],这是摆脱它们的唯一一致方法。

但是,无法评论为什么显式投射到(unsigned)在您发布的情况下可以解决这个问题。充其量是奇特的,我怀疑你在其他情况下是否也会那么幸运。

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

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

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

最新更新