无法绕过gcc的-Wconversion

vva*_*hev 9 c gcc

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更严格.)

问题:

  • 我怎样才能说服GCC编写案例2?
  • 但是,为什么这种正确的转变会改变一切?根据我的理解,给定一个具有unsigned int类型的表达式,位移操作不应改变其类型.
  • 最后,这可能是编译器错误吗?

注意[1]:这个问题不是重复的:c ++位字段和-Wconversion, 因为那里提出的解决方案在我的情况下根本不起作用.

注意[2]:这个问题不是重复: 为什么>> 24导致-Wconversion但是>> 23不?因为引用了一个不同的 bug(或同一个core-bug的不同表现形式)并且使用了一个简单的解决方法,比如在c ++位字段和-Wconversion中提出的,至少使用GCC 7.3.

vva*_*hev 4

我刚刚发现 GCC 的错误跟踪器中有几个与-Wconversion. 特别是:https ://gcc.gnu.org/bugzilla/show_bug.cgi?id=39170

具体来说,评论#18https://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)

PSkocik 建议的解决方法

   s.a = (unsigned){(val >> 12)} & 0xfffff; // works
Run Code Online (Sandbox Code Playgroud)

这是我现在最喜欢的。