为了查看这一点,我编写了这个简单的代码,在其中我创建了不同类型的变量,并将它们按值,按引用和按指针传递给函数:
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约定,所以我假设正在进行的事情与保留调用方保存的寄存器,然后将参数压入堆栈有关。因此,我期望看到的东西都被加载到寄存器中并“推”到任何地方,但是不知道mov
s和lea
s发生了什么。另外,我也不知道是什么DWORD PTR
。
我只了解了registerseax, ebx, ecx, edx, esi, edi, esp
和ebp
,所以看到类似XMM0
或DL
仅仅是让我感到困惑的东西。我想知道lea
何时通过引用/指针传递是有意义的,因为它们使用内存地址,但是我实际上无法确定发生了什么。当涉及到按值传递时,似乎有很多指令,因此这可能与将值复制到寄存器有关。不知道何时将数组作为参数传递和访问。
如果有人可以向我解释有关每个组装块发生了什么的一般想法,我将不胜感激。
使用CPU寄存器传递参数比使用内存(即堆栈)要快。但是,CPU中的寄存器数量有限(尤其是在x86兼容的CPU中),因此,当函数具有许多参数时,将使用堆栈代替CPU寄存器。在您的情况下,有5个函数参数,因此编译器将堆栈用作参数而不是寄存器。
原则上,编译器可以使用push
指令将参数推入堆栈,然后再执行实际call
操作,但是许多编译器(包括gnu c ++)都使用指令将参数mov
推入堆栈。这种方法很方便,因为它不会在调用函数的代码部分中更改ESP寄存器(堆栈的顶部)。
如果passByValue(i, c, p, f, tc)
参数值放在堆栈中。您可以看到mov
从存储器位置到寄存器以及从寄存器到堆栈的适当位置的许多指令。原因是x86程序集禁止直接从一个内存位置移动到另一个内存位置(例外是movs
将值从一个数组(或所需的字符串)移动到另一个)。
如果passByReference(i, c, p, f, tc)
您看到许多5条lea指令,它们会将自变量的地址复制到CPU寄存器,并且这些寄存器的值将移入堆栈。
的情况passByPointer(&i, &c, &p, &f, &tc)
类似于passByValue(i, c, p, f, tc)
。在内部,在汇编级别,按引用传递使用指针,而在较高的C ++级别,程序员不需要在引用上显式使用&
and*
运算符。
将参数移至堆栈后call
,将发出指令,这会将指令指针压EIP
入堆栈,然后再将程序执行转移到子例程。指令之后,moves
堆栈中的所有参数都将用于EIP
堆栈call
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句