C++ 当我使用void函数的返回值(通过转换函数指针)时,会发生什么?
当我运行以下程序时,它总是打印“是”。但是,当我将C++ 当我使用void函数的返回值(通过转换函数指针)时,会发生什么?,c++,assembly,visual-c++,x86-64,undefined-behavior,C++,Assembly,Visual C++,X86 64,Undefined Behavior,当我运行以下程序时,它总是打印“是”。但是,当我将某个常量更改为-2时,它总是打印“否”。为什么呢?我正在使用禁用优化的visual studio 2019编译器 #define SOME_CONSTANT -3 void func() { static int i = 2; int j = SOME_CONSTANT; i += j; } void main() { if (((bool(*)())func)()) { printf("yes
某个常量
更改为-2
时,它总是打印“否”。为什么呢?我正在使用禁用优化的visual studio 2019编译器
#define SOME_CONSTANT -3
void func() {
static int i = 2;
int j = SOME_CONSTANT;
i += j;
}
void main() {
if (((bool(*)())func)()) {
printf("yes\n");
}
else {
printf("no\n");
}
}
编辑:这是func
(IDA Pro 7.2)的输出程序集:
以下是main
的第一部分:
sub rsp, 628h
mov rax, cs:__security_cookie
xor rax, rsp
mov [rsp+628h+var_18], rax
call ?func@@YAXXZ ; func(void)
test eax, eax
jz short loc_1400012B0
call ?func@@YAXXZ ; func(void)
test eax, eax
jz short loc_1400012B0
以下是主要的反编译:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
func();
if ( v3 )
printf("yes\n");
else
printf("no\n");
return 0;
}
我总是打印出
no
,因此它必须依赖于不同的编译器,因此最好的答案是UB(未定义的行为)
此表达式获取指向func
的指针,将指针强制转换为其他类型的函数,然后调用它。通过指向函数签名与原始函数不匹配的函数的指针调用函数是未定义的行为,这意味着可能发生任何事情。从函数调用发生的那一刻起,程序的行为就无法进行推理。你无法确定地预测会发生什么。在不同的优化级别、不同的编译器、同一编译器的不同版本上,或者针对不同的体系结构时,行为可能会有所不同
这仅仅是因为编译器可以假定您不会这样做。当编译器的假设和现实发生冲突时,结果就是一个真空,编译器可以在其中插入它喜欢的任何东西
你的问题“为什么会这样?”的简单答案很简单:因为它可以。但明天它可能会做些别的事情。显然发生的是:
mov ecx, cs:i
add ecx, eax
mov eax, ecx ; <- final value of i is stored in eax
mov cs:i, eax ; and then also stored in i itself
rax
(或其一部分,如eax
或al
)用于WIN64 ABI中整型ish类型(如布尔型)的返回值,这是有意义的。这意味着,i
的最终值碰巧被用作返回值,这是偶然的。简短回答:未定义的行为。更长的回答:。未定义的行为是未定义的。该程序还不如删除你的硬盘drive@super没关系。如果它调用未定义的行为-任何事情都可能发生。这个C++代码没有多大意义。你需要看一下大会来了解正在发生的事情。个人对UB的推测可能很有趣,但事实并非如此。检查程序集以查看编译器执行了什么操作,并检查其源代码以了解为什么可能执行此操作。另外,main()
返回int
。这看起来像(部分?)未优化的编译器输出,否则即使是MSVC也可以优化掉大部分或全部func
函数体。这就解释了冗余的mov-eax,ecx
;编译器甚至没有尝试。奇怪的是,它在调试版本中没有将RBP设置为帧指针。@PeterCordes它没有优化,正如OP所指出的,它从函数调用发生的那一刻起就被复制(优化版本直接从i
中减去,而不污染任何寄存器),程序的行为无法得到解释。更糟糕的是,(陈雷蒙著)。
mov ecx, cs:i
add ecx, eax
mov eax, ecx ; <- final value of i is stored in eax
mov cs:i, eax ; and then also stored in i itself
call ?func@@YAXXZ ; func(void)
test eax, eax
jz short loc_1400012B0