Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 当我使用void函数的返回值(通过转换函数指针)时,会发生什么?_C++_Assembly_Visual C++_X86 64_Undefined Behavior - Fatal编程技术网

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