分配给位域时出现 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 打开来跟踪这个。
我正在尝试解决与 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 打开来跟踪这个。