c++中函数参数的实际情况

What actually happens in C++ with function parameters?

本文关键字:实际情况 参数 函数 c++      更新时间:2023-10-16

在过去的几个月里我一直在学习c++。我知道对于函数,你首先声明参数,像这样:

int myFunc(int funcVar);

,然后你可以传递一个整型变量给这个函数,像这样:

int x = 5;
myFunc(x);

当传递一个参数给一个函数时,我通常认为它就像赋值并复制x的值到myFunc的形参中,在c++中看起来像这样:

funcVar = x;

然而,我注意到当声明有引用(或指针)形参的函数时:

int myFunc(int & funcVar);
我可以将变量x传递给myFunc:
myFunc(x);

看起来像(在我看来):

&funcVar = x;

或者你可以传递一个实际的引用作为参数

int & rX = x;
myFunc(rX);

和函数将工作得很好,我认为它看起来像c++

中的这条语句
int & funcVar = rX

将引用赋值给引用是没有意义的。我的问题是,编译器实际上是如何在函数中加载参数的?我不应该把它看作是把变量的值赋给函数的参数吗?

调用函数时,函数的每个参数都被初始化(未赋值)。此操作的规则与任何其他复制初始化的规则相同。所以如果你有

int myFunc(int funcVar);
int x = 5;
myFunc(x);

funcVar初始化,就像通过这样的语句:

int funcVar = x;

如果你有

int myFunc(int & funcVar);
myFunc(x);
int & rX = x;
myFunc(rX);

funcVar初始化(并且未赋值),就像这样的语句:

int & funcVar = x;
int & funcVar = rX;

引用的初始化将其绑定到初始化器所表示的对象或函数。第二个初始化是有意义的——表达式 rX表示对象 x,因为rX是绑定到x的引用。因此,使用rX初始化引用与使用x初始化引用具有相同的效果。

让我们编写简单的代码并进行反汇编。

int by_value(int x) { return x; }
int by_reference(int &x) { return x; }
int by_pointer(int *x) { return *x; }
int main()
{
    int x = 1;
    by_value(x);
    by_reference(x);
    by_pointer(&x);
    return 0;
}

$ g++ -g -O0 a.cpp ; objdump -dS a.out

在我的环境(x86_64, g++ (SUSE Linux) 4.8.3 20140627)中,结果如下:(全文在这里http://ideone.com/Z5G8yz)

00000000004005dd <_Z8by_valuei>:
int by_value(int x) { return x; }
  4005dd:       55                      push   %rbp
  4005de:       48 89 e5                mov    %rsp,%rbp
  4005e1:       89 7d fc                mov    %edi,-0x4(%rbp)
  4005e4:       8b 45 fc                mov    -0x4(%rbp),%eax
  4005e7:       5d                      pop    %rbp
  4005e8:       c3                      retq   
00000000004005e9 <_Z12by_referenceRi>:
int by_reference(int &x) { return x; }
  4005e9:       55                      push   %rbp
  4005ea:       48 89 e5                mov    %rsp,%rbp
  4005ed:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  4005f1:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4005f5:       8b 00                   mov    (%rax),%eax
  4005f7:       5d                      pop    %rbp
  4005f8:       c3                      retq   
00000000004005f9 <_Z10by_pointerPi>:
int by_pointer(int *x) { return *x; }
  4005f9:       55                      push   %rbp
  4005fa:       48 89 e5                mov    %rsp,%rbp
  4005fd:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  400601:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  400605:       8b 00                   mov    (%rax),%eax
  400607:       5d                      pop    %rbp
  400608:       c3                      retq   
0000000000400609 <main>:
int main()
{
  400609:       55                      push   %rbp
  40060a:       48 89 e5                mov    %rsp,%rbp
  40060d:       48 83 ec 10             sub    $0x10,%rsp
        int x = 1;
  400611:       c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
        by_value(x);
  400618:       8b 45 fc                mov    -0x4(%rbp),%eax
  40061b:       89 c7                   mov    %eax,%edi
  40061d:       e8 bb ff ff ff          callq  4005dd <_Z8by_valuei>
        by_reference(x);
  400622:       48 8d 45 fc             lea    -0x4(%rbp),%rax
  400626:       48 89 c7                mov    %rax,%rdi
  400629:       e8 bb ff ff ff          callq  4005e9 <_Z12by_referenceRi>
        by_pointer(&x);
  40062e:       48 8d 45 fc             lea    -0x4(%rbp),%rax
  400632:       48 89 c7                mov    %rax,%rdi
  400635:       e8 bf ff ff ff          callq  4005f9 <_Z10by_pointerPi>
        return 0;
  40063a:       b8 00 00 00 00          mov    $0x0,%eax
}

by_reference(x)与by_pointer(&x)相同!

将一个引用赋值给另一个引用(当第一次定义它时,即在初始化时)是完全有意义的,这就是实际发生的情况。一个引用只是一个别名,所以当你把一个引用分配给另一个引用时,你只是说第一个引用是你分配的别名。示例

int x = 42;
int& rx = x;
int& ry = rx;
++ry;
std::cout << x; // displays 43

Live on Coliru

相关文章: