c++中函数参数的实际情况
What actually happens in C++ with function parameters?
在过去的几个月里我一直在学习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
- 在没有参数列表的情况下使用模板名称"Event"无效,模板问题
- 在不传递参数数量且只有3个点的情况下,如何使用变差函数
- G++ C++17 类模板参数推导在非常特殊的情况下不起作用
- 如何在不知道C++中有多少可选参数的情况下在循环中使用va_arg?
- 如何在不强制转换每个参数的情况下删除初始值设定项列表中从 int 到 char 的缩小转换?
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- 如何在不传递命令行参数的情况下在 c++ 中设置环境变量
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- 是否可以在不填充自己的参数的情况下将模板函数作为参数传递?
- 我应该如何在没有变量的情况下将相同的参数传递给 CMAKE 中的多个目标?
- C++复杂情况的比较器通过参数问题
- 是否可以在不扣除的情况下将模板参数转发到 make_*?
- 为什么或在什么情况下,你会将参数作为C++中的引用(或指针)传递给函数?
- 我能否获得正式模板参数而不是实际模板参数的字符串表示形式
- 在CPU中花费的时间比实际情况更快
- 为什么Crypto++认为我的消息比实际情况更大
- c++中函数参数的实际情况
- 在什么实际情况下bool(std::ifstream) != std::ifstream::good()
- visual当在C++中删除不完整的类型不是错误时,是否存在实际情况
- 在 C 样式类型转换期间,关于强制转换对象的地址空间的实际情况