【问题标题】:Shifting unsigned int more than the size of it, undefined or not?移动 unsigned int 超过它的大小,未定义与否?
【发布时间】:2023-03-27 04:19:02
【问题描述】:

2011 年草案说:

6.5.7 移位运算符/4 E1

J.2 未定义的行为 表达式移动负数或移动量大于或等于 提升表达 (6.5.7)。

如何解释两者? J.2 是否指所有移位(无符号或无符号)或在第 6.5.7 节中明确提到的 UB(仅适用于有符号)。

我的意思是,unsigned int i=...; i <<= sizeof(i)*CHAR_BIT;UB?

【问题讨论】:

  • J.2 指所有移位,结果未定义。一些实现选择评估(uint32_t)x << 32x,一些评估到0。也许42 会很好;)
  • @Jean-BaptisteYunès 不,char 可以有超过 8 位 并且 unsigned int 允许有填充位。跨度>
  • @Jean-BaptisteYunès 好吧,大多数 机器,但是在具体询问 UB 时这有什么帮助? ;)
  • 即使你想通过使用可变的移位次数来“欺骗”它 - val <<= shift; 它将被转换为:x86 shl eax,cl x64 shl rax,cl 并且那些机器代码指令可以不要移动 32 或 64 次,因为第一个只需要 5 位,第二个 6 位来自 cl 寄存器。因此 val 将在移位后保持其初始值。
  • x86 也有不同的化身。原来的 8086/8088确实移动了CL 给出的全部位数。因此,如果您有一台 PC/XT 和一台 PC/AT,您可以从同一个可执行文件中得到不同的结果。 UB 的另一个原因。

标签: c language-lawyer


【解决方案1】:

你引用的上面那段说了同样的话,不管签名:

6.5.7 移位运算符 / 3 整数提升在每个操作数上执行。结果的类型是 提升的左操作数。如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

所以,它是 UB,无论是未签名的还是签名的。

【讨论】:

  • 谢谢,我错过了!
【解决方案2】:

标准很明确,这确实是UB。如果你信任你的编译器,你也可以通过这个小测试程序来找出答案:

#include <stdio.h>

#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
                  + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))

#define UINT_BITS IMAX_BITS((unsigned)-1)

int main(void)
{
    unsigned int foo = 42;
    printf("%d", foo << UINT_BITS);
    return 0;
}

看看会发生什么:

$ gcc -std=c11 -Wall -Wextra -pedantic -oshift shift.c
shift.c: In function 'main':
shift.c:11:22: warning: left shift count >= width of type [-Wshift-count-overflow]
     printf("%d", foo << UINT_BITS);
                      ^~
$ ./shift
42

【讨论】:

    猜你喜欢
    • 2022-08-04
    • 1970-01-01
    • 1970-01-01
    • 2018-10-13
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多