【发布时间】:2017-12-21 23:56:27
【问题描述】:
对于input 0xffffffff,以下c 代码在没有优化的情况下工作正常,但在使用-O1 编译时会产生错误结果。其他编译选项是 -g -m32 -Wall。该代码在 macOS 10.13.2 中使用 clang-900.0.39.2 进行了测试。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 2) return 1;
char *endp;
int x = (int)strtoll(argv[1], &endp, 0);
int mask1 = 0x55555555;
int mask2 = 0x33333333;
int count = (x & mask1) + ((x >> 1) & mask1);
int v1 = count >> 2;
printf("v1 = %#010x\n", v1);
int v2 = v1 & mask2;
printf("v2 = %#010x\n", v2);
return 0;
}
输入:0xffffffff
-O0 的输出:(预期)
v1 = 0xeaaaaaaa
v2 = 0x22222222
-O1 的输出:(错误)
v1 = 0x2aaaaaaa
v2 = 0x02222222
下面是“int v1 = count >> 2;”行的反汇编指令使用 -O0 和 -O1。
使用 -O0:
sarl $0x2, %esi
使用-O1:
shrl $0x2, %esi
以下是“int v2 = v1 & mask2;”行的反汇编指令使用 -O0 和 -O1。
使用 -O0:
andl -0x24(%ebp), %esi //-0x24(%ebp) 存储 0x33333333
使用-O1:
andl $0x13333333, %esi //优化为什么会从0x33333333变成0x13333333?
此外,如果 x 在本地设置为 0xffffffff 而不是从参数中获取其值,则即使使用 -O1,代码也会按预期工作。
P.S:代码是基于我在 CS:APP 课程@CMU 中对数据实验室的解决方案的实验性代码。实验室要求学生实现一个函数,该函数计算 int 变量的 1 位的个数,不使用除 int 以外的任何类型。
【问题讨论】:
-
0xffffffffis >MAX_INT在您的情况下,int的溢出是未定义的行为。 -
@Stargateur 你确定吗?这不应该是-1吗?
-
也就是说,出于多种原因,我建议您使用
unsigned int,位运算符只能与unsigned integer、printf("v1 = %#010x\n", v1);一起使用=>%x期望unsigned int所以它是未定义的发送int的行为。 -
但我认为问题在于右移负符号类型是实现定义的(显然 clang 和 gcc 处理方式不同)。将位操作限制为无符号类型可能最安全:stackoverflow.com/questions/4009885/…
-
@Stargateur:将过大的整数值转换为有符号整数类型不是未定义的行为。它是实现定义的行为。有符号溢出在计算算术运算符期间发生时会导致未定义的行为(这是您的链接所谈论的内容)。 转换到有符号整数类型期间的溢出是实现定义的 (port70.net/~nsz/c/c11/n1570.html#6.3.1.3p3)。只有浮点值在转换为整数类型时会导致 UB。