c++ - 汇编如何做参数传递: by value,引用,不同类型/数组的指针?

标签 c++ c arrays assembly x86

为了了解这一点,我编写了这个简单的代码,其中我只是创建了不同类型的变量,并通过值、引用和指针将它们传递给函数:

int i = 1;
char c = 'a';
int* p = &i;
float f = 1.1;
TestClass tc; // has 2 private data members: int i = 1 and int j = 2

函数体留空,因为我只是在查看参数是如何传入的。

passByValue(i, c, p, f, tc); 
passByReference(i, c, p, f, tc); 
passByPointer(&i, &c, &p, &f, &tc);

想看看这对数组有何不同,以及如何访问参数。

int numbers[] = {1, 2, 3};
passArray(numbers); 

组装:

passByValue(i, c, p, f, tc)

mov EAX, DWORD PTR [EBP - 16]
    mov DL, BYTE PTR [EBP - 17]
    mov ECX, DWORD PTR [EBP - 24]
    movss   XMM0, DWORD PTR [EBP - 28]
    mov ESI, DWORD PTR [EBP - 40]
    mov DWORD PTR [EBP - 48], ESI
    mov ESI, DWORD PTR [EBP - 36]
    mov DWORD PTR [EBP - 44], ESI
    lea ESI, DWORD PTR [EBP - 48]
    mov DWORD PTR [ESP], EAX
    movsx   EAX, DL
    mov DWORD PTR [ESP + 4], EAX
    mov DWORD PTR [ESP + 8], ECX
    movss   DWORD PTR [ESP + 12], XMM0
    mov EAX, DWORD PTR [ESI]
    mov DWORD PTR [ESP + 16], EAX
    mov EAX, DWORD PTR [ESI + 4]
    mov DWORD PTR [ESP + 20], EAX
    call    _Z11passByValueicPif9TestClass


passByReference(i, c, p, f, tc)

    lea EAX, DWORD PTR [EBP - 16]
    lea ECX, DWORD PTR [EBP - 17]
    lea ESI, DWORD PTR [EBP - 24]
    lea EDI, DWORD PTR [EBP - 28]
    lea EBX, DWORD PTR [EBP - 40]
    mov DWORD PTR [ESP], EAX
    mov DWORD PTR [ESP + 4], ECX
    mov DWORD PTR [ESP + 8], ESI
    mov DWORD PTR [ESP + 12], EDI
    mov DWORD PTR [ESP + 16], EBX
    call    _Z15passByReferenceRiRcRPiRfR9TestClass

passByPointer(&i, &c, &p, &f, &tc)

    lea EAX, DWORD PTR [EBP - 16]
    lea ECX, DWORD PTR [EBP - 17]
    lea ESI, DWORD PTR [EBP - 24]
    lea EDI, DWORD PTR [EBP - 28]
    lea EBX, DWORD PTR [EBP - 40]
    mov DWORD PTR [ESP], EAX
    mov DWORD PTR [ESP + 4], ECX
    mov DWORD PTR [ESP + 8], ESI
    mov DWORD PTR [ESP + 12], EDI
    mov DWORD PTR [ESP + 16], EBX
    call    _Z13passByPointerPiPcPS_PfP9TestClass

passArray(numbers)

    mov EAX, .L_ZZ4mainE7numbers
    mov DWORD PTR [EBP - 60], EAX
    mov EAX, .L_ZZ4mainE7numbers+4
    mov DWORD PTR [EBP - 56], EAX
    mov EAX, .L_ZZ4mainE7numbers+8
    mov DWORD PTR [EBP - 52], EAX
    lea EAX, DWORD PTR [EBP - 60]
    mov DWORD PTR [ESP], EAX
    call    _Z9passArrayPi

    // parameter access
    push    EAX
    mov EAX, DWORD PTR [ESP + 8]
    mov DWORD PTR [ESP], EAX
    pop EAX

我假设我正在查看与参数传递有关的正确程序集,因为每个程序的末尾都有调用!

但由于我对组装的了解非常有限,我无法判断这里发生了什么。我了解了 ccall 约定,所以我假设正在发生的事情与保留调用者保存的寄存器然后将参数插入堆栈有关。正因为如此,我期待看到加载到寄存器中的东西并“推送”到任何地方,但不知道 movs 和 leas 发生了什么。另外,我不知道 DWORD PTR 是什么。

我只了解了寄存器:eax、ebx、ecx、edx、esi、edi、espebp,所以看到类似 XMM0DL 也让我感到困惑。我想在通过引用/指针传递时看到 lea 是有意义的,因为它们使用内存地址,但我实际上无法判断发生了什么。当谈到按值传递时,似乎有很多指令,所以这可能与将值复制到寄存器中有关。不知道数组是如何作为参数传递和访问的。

如果有人可以向我解释每个组装 block 的大致情况,我将不胜感激。

最佳答案

使用 CPU 寄存器传递参数比使用内存(即堆栈)更快。然而,CPU 中的寄存器数量有限(尤其是在 x86 兼容的 CPU 中),因此当函数有很多参数时,将使用堆栈而不是 CPU 寄存器。在您的情况下,有 5 个函数参数,因此编译器使用堆栈作为参数而不是寄存器。

原则上编译器可以使用 push 指令在实际 call 运行之前将参数推送到堆栈,但是许多编译器(包括 gnu c++)使用 mov 将参数推送到堆栈。这种方式很方便,因为它不会更改调用函数的代码部分中的 ESP 寄存器(堆栈顶部)。

passByValue(i, c, p, f, tc) 的情况下,参数的值被放置在堆栈上。您可以看到许多 mov 指令从内存位置到寄存器以及从寄存器到堆栈的适当位置。这样做的原因是 x86 程序集禁止从一个内存位置直接移动到另一个内存位置(movs 异常(exception),它将值从一个数组(或您希望的字符串)移动到另一个)。

passByReference(i, c, p, f, tc) 的情况下,您可以看到许多 5 lea 指令将参数的 地址 复制到 CPU 寄存器,这些寄存器的值被移入堆栈。

passByPointer(&i, &c, &p, &f, &tc)的情况与passByValue(i, c, p, f, tc)类似。在内部,在汇编级别,按引用传递使用指针,而在更高的 C++ 级别上,程序员不需要在引用上显式使用 &* 运算符.

参数入栈后调用,将指令指针EIP压入栈,然后将程序执行转移到子程序。在 call 指令之后,所有参数到堆栈的 moves 说明了堆栈上即将到来的 EIP

关于c++ - 汇编如何做参数传递: by value,引用,不同类型/数组的指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19858980/

相关文章:

C:如何按其元素之一对结构进行排序?不能使用指针

javascript - AngularJS HTTP GET 请求有时返回的不是最新的数据集

python - 通过符号将实值 numpy 数组转换为二进制数组

未调用 C++ 构造函数

C++解释器: How to emit error messages?

c++ - OpenCV cv::cvtColor 删除 alpha channel ,如何保留 alpha 数据?

php - 使用 PDO 将数据选择到多维数组中

c++ - wregex 有什么问题?wregex 不支持 native c++ 中的 [group] 功能?

c - 使用 Atmel 89C2051 微 Controller 开发固件应用程序需要哪些软件?

c - 如何解决指针错误?