-1 c gcc integer-overflow
//filename:mt.c\n//filename is useful to understand the gcc command\n#include <stdio.h>\nint isTmax(int x);\n\nint main()\n{\n printf("wtf %d\\n", isTmax(0x7fffffff));\n return 1;\n}\n\nint isTmax(int x)\n{\n int y = ((x + x + 2) ^ 1);\n int z = (!(~(x + x + 2) + 1) ^ 0);\n printf("y = %d\\n", y);\n printf("z = %d\\n", z);\n return y & z;\n} \nRun Code Online (Sandbox Code Playgroud)\n代码很奇怪,因为它是一个 csapp 讲义解决方案(显然是错误的)。(x+x+2)当 x 等于 0x7fffffff 时,\n 等于 0。(~(x+x+2)+1)由于溢出所以等于0。所以(!(~(x+x+2)+1)^0)等于1。调试时观察验证了这一点。\n我认为,正常情况下,赋值后z应该为1。
环境\xef\xbc\x9a{系统\xef\xbc\x9awindows 10; 虚拟系统ubuntu 20.04 LTS;虚拟机软件:VirtualBox 6.1;GCC:gcc(Ubuntu 9.3.0-17ubuntu1~20.04)9.3.0}
\n
这张图片有更多细节。VScode正在连接上述虚拟系统。
\n
完全相同的代码在另一个环境中表现不同。另一种环境\xef\xbc\x9a{系统:windows 10; 虚拟系统:ubuntu 20.04 LTS;虚拟机软件:VirtualBox 6.1;GCC:gcc(Ubuntu 5.4.0-6ubuntu1~16.04.12)5.4.0 20160609}
\n
有符号整数溢出是未定义的行为,因此您的代码无效。如果我们看一个例子
int isTmax(int x)
{
int z = (!(~(x + x + 2) + 1) ^ 0);
return z;
}
Run Code Online (Sandbox Code Playgroud)
其汇编输出如下:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-20], edi
cmp DWORD PTR [rbp-20], -1
sete al
movzx eax, al
mov DWORD PTR [rbp-4], eax
mov eax, DWORD PTR [rbp-4]
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
相关部分是cmp指令,它将DWORD PTR [rbp-20](即参数x)与 -1 进行比较。它不进行计算,而是x与 -1 进行比较。如果x是 -1,则计算结果为:
!(~(-1 + -1 + 2) + 1)
-> !(~(-2 + 2) + 1)
-> !(~0 + 1)
-> !(-1 + 1)
-> !0
-> 1
Run Code Online (Sandbox Code Playgroud)
由于有符号整数溢出是未定义的行为,因此编译器不必考虑任何x会导致溢出的值。所以如果x不是-1,你最终会得到
!(~(non-zero) + 1)
-> !((non-negative-one) + 1)
-> !(non-zero)
-> 0
Run Code Online (Sandbox Code Playgroud)
切换x为无符号后,编译器现在必须考虑溢出(因为它是允许的)。所以该函数编译为:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-20], edi
mov eax, DWORD PTR [rbp-20]
add eax, 1
add eax, eax
neg eax
test eax, eax
sete al
movzx eax, al
mov DWORD PTR [rbp-4], eax
mov eax, DWORD PTR [rbp-4]
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
在这里,它会按照您的预期加载x并eax进行计算。