C ++汇编程序输出 - 如何实现引用

时间:2013-12-30 00:11:29

标签: c++ assembly reference

c ++和汇编程序的工具链:GNU

我有以下C ++代码:

int main(void)
{
  int i = 33, j = 66;
  swap(i,j);

  cout << i << ", " << j << endl;

  return(0);
}

如果我现在检查生成的汇编代码,我会在swap区域内调用以下内容:

movl    $33, -24(%rbp)
movl    $66, -20(%rbp)
leaq    -20(%rbp), %rdx
leaq    -24(%rbp), %rax
movq    %rdx, %rsi
movq    %rax, %rdi
call    _ZSt4swapIiEvRT_S1_
movl    -20(%rbp), %ebx
movl    -24(%rbp), %eax

交换例程本身就是:

_ZSt4swapIiEvRT_S1_:
.LFB1232:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movq    %rdi, -24(%rbp)
movq    %rsi, -32(%rbp)
movq    -24(%rbp), %rax
movl    (%rax), %eax
movl    %eax, -4(%rbp)
movq    -32(%rbp), %rax
movl    (%rax), %edx
movq    -24(%rbp), %rax
movl    %edx, (%rax)
movq    -32(%rbp), %rax
movl    -4(%rbp), %edx
movl    %edx, (%rax)
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc

所以 - 因为我不熟悉汇编程序 - 这是否意味着对于引用,变量的地址直接赋予函数?这意味着它们不会通过堆栈传递(这是有道理的,因为引用应该是什么)?

这是做什么的? (在电话周边地区):

movq    %rdx, %rsi
movq    %rax, %rdi

3 个答案:

答案 0 :(得分:5)

引用只不过是一个指针,“只是”用不同的符号。

因此,无论您使用指针还是引用

,最终生成的汇编代码都是相同的
void swap1(int &a, int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

void swap2(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
_Z5swap1RiS_:
.LFB0:
    .cfi_startproc
    movl    (%rdi), %eax
    movl    (%rsi), %edx
    movl    %edx, (%rdi)
    movl    %eax, (%rsi)
    ret
    .cfi_endproc

_Z5swap2PiS_:
.LFB1:
    .cfi_startproc
    movl    (%rdi), %eax
    movl    (%rsi), %edx
    movl    %edx, (%rdi)
    movl    %eax, (%rsi)
    ret
    .cfi_endproc

答案 1 :(得分:2)

就编译器而言,引用只是指针。两者之间的区别完全在于程序员如何使用它们。

答案 2 :(得分:0)

很抱歉写了一个很长的答案,但我认为汇编是了解编译器如何实现引用的最佳方法。

#include <iostream>

using namespace std;

int main()
{
    int i = 10;
    int *ptrToI = &i;
    int &refToI = i;

    cout << "i = " << i << "\n";
    cout << "&i = " << &i << "\n";

    cout << "ptrToI = " << ptrToI << "\n";
    cout << "*ptrToI = " << *ptrToI << "\n";
    cout << "&ptrToI = " << &ptrToI << "\n";

    cout << "refToNum = " << refToI << "\n";
    //cout << "*refToNum = " << *refToI << "\n";
    cout << "&refToNum = " << &refToI << "\n";

    return 0;
}

此代码的输出就像这样

i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8

让我们看一下反汇编(我为此使用了GDB。这里的8,9和10是代码行号)

8           int i = 10;
0x08048698 <main()+18>: movl   $0xa,-0x10(%ebp)

此处$0xa是我们分配给i的10(十进制)。 -0x10(%ebp)这里表示ebp寄存器-16(十进制)的内容。 -0x10(%ebp)指向堆栈上i的地址。

9           int *ptrToI = &i;
0x0804869f <main()+25>: lea    -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov    %eax,-0x14(%ebp)

i的地址指定给ptrToIptrToI位于地址-0x14(%ebp)的堆栈上,即ebp - 20(十进制)。

10          int &refToI = i;
0x080486a5 <main()+31>: lea    -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov    %eax,-0xc(%ebp)

现在抓住了!比较第9行和第10行的反汇编,您将观察到,-0x14(%ebp)被第10行中的-0xc(%ebp)替换。-0xc(%ebp)refToNum的地址。它在堆栈上分配。但是,您永远无法从代码中获取此地址,因为您无需知道地址。

因此;引用确实占用了内存。在这种情况下,它是堆栈内存,因为我们已将其分配为局部变量。它占用了多少内存?指针占据了很多。

现在让我们看看我们如何访问引用和指针。为简单起见,我只展示了汇编代码段的一部分

16          cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>:        mov    -0x14(%ebp),%eax
0x08048749 <main()+195>:        mov    (%eax),%ebx
19          cout << "refToNum = " << refToI << "\n";
0x080487b0 <main()+298>:        mov    -0xc(%ebp),%eax
0x080487b3 <main()+301>:        mov    (%eax),%ebx

现在比较以上两行,你会看到惊人的相似性。 -0xc(%ebp)refToI的实际地址,您永远无法访问该地址。简单来说,如果您将引用视为普通指针,那么访问引用就像获取引用指向的地址处的值一样。这意味着以下两行代码将为您提供相同的结果

cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";

现在比较一下

15          cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>:        mov    -0x14(%ebp),%ebx
21          cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>:        mov    -0xc(%ebp),%eax

我想你能发现这里发生了什么。如果您要求&refToI,则会返回-0xc(%ebp)地址位置的内容,-0xc(%ebp)位于refToi所在的位置,其内容只是i的地址。

最后一件事,为什么这一行被评论了?

//cout << "*refToNum = " << *refToI << "\n";

因为不允许*refToI,它会给你一个编译时错误。