【问题标题】:Unable to bypass gcc's -Wconversion无法绕过 gcc 的 -Wconversion
【发布时间】:2019-01-29 13:04:14
【问题描述】:
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 编译一个项目,但遇到了一个无法让编译器相信我可以进行转换的情况。

  • 在情况 1 中,我使用的是在 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? 因为指的是 不同 错误(或相同核心错误的不同表现形式)并且有一个使用强制转换的简单解决方法,就像在 c++ bit fields and -Wconversion 中提出的那样,至少在 GCC 7.3 中。

【问题讨论】:

标签: c gcc


【解决方案1】:

我刚刚发现在 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 bug

个人解决方法

鉴于编译器的错误,我个人的解决方法是将右移操作包装在 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

这是我现在最喜欢的。

【讨论】:

    【解决方案2】:

    A ... 解决方法:使用临时变量。不理想,但它摆脱了警告

    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
    

    【讨论】:

    • 复合文字也可以s.a = (unsigned){(val &gt;&gt; 12)} &amp; 0xfffff;
    • @bolov 你的变通方案确实有效,但我一直在寻找对代码影响最小的东西。在某些情况下,使用临时变量可能非常丑陋/侵入性。 #pragma 更糟糕。不过还是谢谢你的回答。
    • @PSkocik 我在回答中提到了您出色的解决方法(我希望没关系)。如果您使用此解决方法发布自己的答案,当然,我会将其从我的答案中删除并投票赞成您的答案。
    • @vvaltchev 没什么了不起的。只是知道复合文字本质上是匿名变量。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    相关资源
    最近更新 更多