int main() {
struct { unsigned int a:20; } s;
unsigned int val = 0xaabbc000;
s.a = val & 0xfffff; // 1) works
s.a = (val >> 12) & 0xfffff; // 2) generates -Wconversion warning
}
我正在使用-Wconversion
编译项目,但遇到无法使编译器确信转换良好的情况。
在第一种情况下,我使用的是c++ bit fields and -Wconversion中提出的相同解决方案,并且效果很好。由于位掩码限制了值的范围,因此它迫使编译器接受转换。
但是,在情况2中,由于移位(为什么?),编译器拒绝接受转换。并通过以下方式抱怨:
$ gcc wconv.c -Wconversion -Werror
wconv.c: In function ‘main’:
wconv.c:8:11: error: conversion to ‘unsigned int:20’ from ‘unsigned int’ may alter its value [-Werror=conversion]
s.a = (val >> 12) & 0xfffff; // 2) generates -Wconversion warning
^
cc1: all warnings being treated as errors
(有趣的是:使用clang时,代码可以毫无问题地编译。到目前为止,我观察到clang的-Wconversion
比GCC的严格得多。)
问题:
unsigned int
类型的表达式,移位操作不应更改其类型。注意[1] :此问题不是的重复项:c++ bit fields and -Wconversion 因为在那里提出的解决方案对我而言根本行不通。
注意[2] :此问题不是重复: Why >>24 causes -Wconversion but >>23 doesn't?是因为它指的是不同的错误(或同一core-bug的不同表现形式),并且至少在c++ bit fields and -Wconversion中提出了使用强制类型转换的简单解决方法GCC 7.3。
答案 0 :(得分:4)
我刚刚发现,在GCC的错误跟踪器中,有几个与-Wconversion
相关的错误。特别是:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39170
具体来说,评论#18
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39170#c18)报告了一个与我的示例几乎相同的示例:
#include <stdint.h>
struct foo
{
unsigned bar: 30;
unsigned fill: 2;
};
struct foo test(uint32_t value)
{
struct foo foo;
foo.bar = (value >> 2) & 0x3fffffffU;
return foo;
}
因此,我认为这个问题肯定是 gcc错误。
鉴于编译器的错误,我个人的解决方法是将正确的移位操作包装在static
always_inline
函数中,即使我对此hack并不是特别满意。
#include <stdint.h>
static __attribute__((always_inline)) inline uintptr_t
rshift(uintptr_t val, uintptr_t bits)
{
return val >> bits;
}
int main() {
struct { unsigned int a:20; } s;
unsigned int val = 0xaabbc000;
s.a = val & 0xfffff; // 1) works
s.a = (rshift(val, 12)) & 0xfffff; // 2) works
}
s.a = (unsigned){(val >> 12)} & 0xfffff; // works
到目前为止,哪个是我的最爱。
答案 1 :(得分:3)
一个...解决方法:使用临时变量。不理想,但是摆脱了警告
const unsigned t = val >> 12u;
s.a = t & 0xfffffu;
除此之外,您可以显式关闭该行的警告:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
s.a = (val >> 12u) & 0xfffffu;
#pragma GCC diagnostic pop