c++ - 为什么这个函数调用在通过类型转换的函数指针调用它后表现得很好?

标签 c++ gcc function-pointers

我有以下代码。有一个函数需要两个 int32。然后我获取一个指向它的指针并将其转换为一个需要三个 int8 的函数并调用它。我预计会出现运行时错误,但程序运行良好。为什么这甚至可能?

main.cpp:

#include <iostream>

using namespace std;

void f(int32_t a, int32_t b) {
    cout << a << " " << b << endl;
}

int main() {
    cout << typeid(&f).name() << endl;
    auto g = reinterpret_cast<void(*)(int8_t, int8_t, int8_t)>(&f);
    cout << typeid(g).name() << endl;
    g(10, 20, 30);
    return 0;
}

输出:

PFviiE
PFvaaaE
10 20

正如我所见,第一个函数的签名需要两个整数,第二个函数需要三个字符。 Char 比 int 小,我想知道为什么 a 和 b 仍然等于 10 和 20。

最佳答案

正如其他人所指出的那样,这是未定义的行为,因此原则上可能发生的事情都是错误的。但是假设您使用的是 x86 机器,那么对于您为什么会看到这个问题有一个合理的解释。

在 x86 上,g++ 编译器并不总是通过将参数压入堆栈来传递参数。相反,它将前几个参数存储到寄存器中。如果我们反汇编 f 函数,请注意前几条指令将参数移出寄存器并显式移入堆栈:

    push    rbp
    mov     rbp, rsp
    sub     rsp, 16
    mov     DWORD PTR [rbp-4], edi  # <--- Here
    mov     DWORD PTR [rbp-8], esi  # <--- Here
    # (many lines skipped)

同样,请注意调用是如何在 main 中生成的。参数被放入这些寄存器中:

    mov     rax, QWORD PTR [rbp-8]
    mov     edx, 30      # <--- Here
    mov     esi, 20      # <--- Here
    mov     edi, 10      # <--- Here
    call    rax

由于整个寄存器都被用来保存参数,所以参数的大小在这里并不重要。

此外,由于这些参数是通过寄存器传递的,因此无需担心以不正确的方式调整堆栈大小。一些调用约定(cdecl)让调用者进行清理,而另一些(stdcall)要求被调用者进行清理。然而,这两者都不重要,因为堆栈没有被触及。

关于c++ - 为什么这个函数调用在通过类型转换的函数指针调用它后表现得很好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56727050/

相关文章:

c++ - 是否可以检查将程序输出重定向到文件的控制台的宽度?

c++ - 如何使用 .sdf 文件修复 RDkit 中的 `OSError: file error: bad input file`?

linux - .so : need to find out which function(s) are executed on loading

c++ - SHIMVIEW : shiminfo means? 是什么

c++ - "dual inheritance"指针

c - 多文件编译出错时停止 GCC

c++ - std::map 的多态成员变量指针

检查将一个函数指针转换为另一个函数指针是否安全

CUDA 函数指针

c++ - 基于运算符推导模板返回类型? : result