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
}
Run Code Online (Sandbox Code Playgroud)
我正在编译一个项目,-Wconversion我遇到了一个案例,我无法说服编译器我对转换很好.
在案例1中,我使用了在c ++位字段和-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
Run Code Online (Sandbox Code Playgroud)
(有趣的是:有了clang代码编译没有问题.到目前为止,我已经观察到clang -Wconversion比GCC更严格.)
问题:
unsigned int类型的表达式,位移操作不应改变其类型.注意[1]:这个问题不是重复的:c ++位字段和-Wconversion, 因为那里提出的解决方案在我的情况下根本不起作用.
注意[2]:这个问题不是重复: 为什么>> 24导致-Wconversion但是>> 23不?因为引用了一个不同的 bug(或同一个core-bug的不同表现形式)并且使用了一个简单的解决方法,比如在c ++位字段和-Wconversion中提出的,至少使用GCC 7.3.
我刚刚发现 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;
}
Run Code Online (Sandbox Code Playgroud)
因此,我相信这个问题绝对是一个gcc bug。
考虑到编译器的错误,我个人的解决方法是将右移操作包装在一个static always_inline函数中,即使我对这个黑客不是特别满意。
#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
}
Run Code Online (Sandbox Code Playgroud)
s.a = (unsigned){(val >> 12)} & 0xfffff; // works
Run Code Online (Sandbox Code Playgroud)
这是我现在最喜欢的。