无法绕过gcc的-Wconversion

时间:2019-01-29 13:04:15

标签: 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
}

我正在使用-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的严格得多。)

问题:

  • 我如何说服GCC编译案例2?
  • 但是,为什么右移会改变一切呢?以我的理解,给定具有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。

2 个答案:

答案 0 :(得分: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;
}

因此,我认为这个问题肯定是 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
}

PSkocik建议的解决方法

   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