分配给位域时出现 GCC 转换警告之子

Son of GCC conversion warning when assigning to a bitfield

我正在尝试解决与 GCC conversion warning when assigning to a bitfield 几乎完全相同的问题,除了 none 的解决方案似乎有效。对于链接的问题,gcc 版本似乎没有帮助,gccs 10.1、9.1、8.2、8.1、7.1、6.1、5.1 和 4.9.1 都失败了。

typedef unsigned int uint;
struct foo { uint a:8; uint b:24; };
void bar(struct foo num, uint x) {
    num.b = (5U << 1) | (1 & 1);
    num.b = ((uint)(((5U << 1) | (uint)((uint) x & 1))) & 0xffffffU);
    num.a = (unsigned char)x;
}

Watch it fail on Godbolt. 编译器产生:

In function 'bar':
5:13: error: conversion from 'unsigned int' to 'unsigned int:24' may change value [-Werror=conversion]
    5 |     num.b = ((uint)(((5U << 1) | (uint)((uint) x & 1))) & 0xffffffU);
      |             ^

如您所见,我尝试过显式屏蔽为 24 位,将随机内容转换为 unsigned int,以及上述几乎所有组合(例如仅屏蔽、仅转换、在看似任何相关位置转换等) .第一个 num.b 赋值适用于常量,但添加变量会使一切变得混乱。

我已经通过下面的 pragmas 解决了这个问题,但这不是一个非常令人满意的解决方案。

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
    num.b = (5U << 1) | (x & 1U);
#pragma GCC diagnostic pop

@Artyr 在对该问题的评论中提出了解决方案。如果他把它变成一个答案,我会接受它。但是,在调查该解决方案时,我发现了另一种解决 确实 没有意义的问题的方法。在下面的示例中,只有最后一个赋值失败。

uint z = 5U;
num.b = (5U << 1) | (1 & 1); //OK
num.b = ((z << 1) | (x & 1)) & 0xffffffU; //OK
num.b = ((5U << 1) | (x & 1)) & 0xffffffU; //BAD

我不明白的是为什么添加另一个从同一常量静态分配的变量 z 可以解决问题。为什么常量和变量的组合会导致问题?

一天后没有任何答案让我接受,我将总结评论中的发现。

  • @Artyer 发现将变量转换为布尔值可以解决问题。这解决了战术问题,但没有解决更普遍的问题(例如 z & 0xf)。

    num->b = (5U << 1) | (_Bool)(x & 1);
    
  • @M.M 独立发现我假设的结果相同(转换为布尔值)。使用 !! 可以解决问题。同样,这解决了战术问题,但没有解决更普遍的问题(例如 z & 0xf)。

    num->b = (5U << 1) | !!(x & 1);
    
  • 我在研究上述解决方案时发现,将表达式的 "constant" 端存储到变量中通常可以解决该问题。

    unsigned int z = 5U;
    num->b = (z << 1) | (x & 1);
    
  • @yugr 认为这是一个错误。 Bug 95213 用 gcc 打开来跟踪这个。