【问题标题】:compiler warning: left shift of negative value编译器警告:负值左移
【发布时间】:2019-11-06 09:48:34
【问题描述】:

我认为 GCC 错误地产生了 [-Wshift-negative-value] 警告。

我有一个函数应该产生一个由其单个输入参数提供的特定长度的后缀掩码:

#include <stdint.h>

uint16_t get_suffix_mask_sht(uint8_t shift) {
    return (~(~((uint16_t) 0) << shift));
}

我尝试在不同版本的 gcc 上使用以下编译器选项编译此函数

-Werror -Wextra

如果我将输出从 uint16_t 更改为 uint8_t,也会出现此警告。更大的输出类型,即uint32_t 不会产生此警告。

我使用的是旧版本的 GCC:7.4。但是我已经使用最新的 GCC 9.x 版本在godbolt 上进行了尝试,它们都产生了相同的警告。但是,Clang 版本不会产生此错误。

【问题讨论】:

  • 请注意:如果定义如下一行,为什么还要使用原型?
  • 哦,这只是我的编译器抱怨缺少声明的残余。我会删除它。

标签: c gcc gcc-warning


【解决方案1】:

当小于int 的变量(例如uint16_t)与按位补码运算符~ 一起使用时,它们是promoted to int

int 类型是有符号的,不能很好地与移位一起工作。特别是考虑到~0 将是带有二进制补码表示法的-1(这是在二进制系统中表示负数的最常见表示法)。

一种可能的解决方案是使用较大的无符号类型(如unsigned int),然后在完成后使用掩码获取较小类型的相关位。

【讨论】:

    【解决方案2】:

    警告是正确的。

    ~((uint16_t) 0) 结果为负值,因为在执行按位补码之前,(uint16_t) 0 的结果被提升为int

    一般来说,在使用位移位时,您应该更喜欢无符号整数(足够宽以避免类型提升)。我的建议是改用unsigned int zero:

    return (~(~0U << shift));
    

    【讨论】:

    • 啊,感谢您的快速回答!所以我应该使用uint16_t (-1)而不是~((uint16_t) 0)
    • @ArisKoning 不起作用,因为整数提升也会对按位移位运算符的两个操作数进行。
    • 好吧,至少我现在明白问题出在整数提升的规则上。我看看能不能重写一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-10
    相关资源
    最近更新 更多