C/C++学习记录:深入理解三种传参方式

Posted 河边小咸鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++学习记录:深入理解三种传参方式相关的知识,希望对你有一定的参考价值。

C/C++学习记录:深入理解三种传参方式
  之前对传参这方面的东西一直是知其然不知所以然。概念用法怎么用都知道,但是其真正的内部操作流程确实是理解不足。这两天一直在总结shell脚本的笔记,写累了正好研究一下传参这方面的内容。
  这篇笔记中记录了关于这方面我的理解过程和心得。关于本篇笔记的深度,也是到汇编为止不再深入,就我个人理解来看已经是足够了。


一、关于三种传参方式

1. 值传参

1.1 简单总结

  这是我在编程中最早接触的传参方式,也是一开始使用最多的传参方式。它的特点很明确就是简便,非常明了。当然缺点也是被说了很多次,就是慢+占用空间+不能修改实参。因为所谓的值传参是把实参的值复制了一遍,所以会有上面的特点。

1.2 我的疑问

  总是说值传参的执行过程会复制实参的值,那么它的流程是怎么样的?

2. 引用传参

2.1 简单总结

  这是C++里的概念,C里是没有的。它解决了值传参不能修改实参的问题,另外也比传值要快。就我目前接触到的C++代码中,里面均常常用到&const &,例如stl的源码。

2.2 我的疑问

  我看网上说传引用其实也是传的指针,所以一直对引用的流程很有兴趣。如果真的也是传指针,那么它的意义就是更简单明了的传指针吗?另外很多源码中都使用const &,我一直很好奇传引用究竟能比传值快多少。

3. 指针传参

3.1 简单总结

  第一次接触传指针,还是在当时学习链表的时候。在此之前,我对于指针作用的印象仅仅是文件指针和一丢丢字符串的内容,而对于学习中碰到的那些什么*p,&p的完全没有实际应用中的感受,甚至产生了疑问,为何大伙都说指针牛p?
  在接触到链表头结点的指针后,我首次发现原来传值是不能改变内容的(太菜了当时),得传指针,所以链表函数传参时,节点得取个地址传进去,由此我打开了新世界的大门,感受到了指针的牛p。以至于后面再接触java的时候感觉浑身难受,感受到了一种局限感,所以后面我决定以C/C++为方向。
  对我而言,指针传参相当于是一种 “降维打击”,相当于“你收拾不了他就去找他爹收拾他”。总而言之,向下层操作性很大(提领指针的内容),可以修改实参并且速度也很快。但是,传指针相当于把传值的内容改为指针,所以指针层面也是不能被修改的(虽然我也没见过要修改最高层指针),由于指针的大小是固定的而且很小,传指针的速度也会很快。

3.2 我的疑问

  底层流程是什么?是先获取地址,再走值传递那一套流程吗?

二、汇编层面剖析

1. 操作

  我的理解方式是通过vs2019的反汇编功能查看低层汇编代码进行比对分析,而下面是我的操作过程。
  首先是实验源码如下,可以看到我声明了三个函数,分别用了三种传参方法。

/*
* 三种传参方式测试
* 2021/8/22
*/
#include<cstdio>

//值传参
void func_value(int x)
{
	x = 22;
}

//引用传参
void func_ref(int& x_ref)
{
	x_ref = 2222;
}

//指针传参
void func_ptr(int* x_ptr)
{
	*x_ptr = 22222;
}

int main()
{
	int test_arg = 222;
	//值传参
	func_value(test_arg);
	//引用传参
	func_ref(test_arg);
	//指针传参
	func_ptr(&test_arg);
	return 0;
}

  接着,我开启调试反汇编,查看调用三个函数时的汇编源码,结果如下:

2. 总结

   说实话,我没想到传引用和传指针的汇编源码竟然完全一样…而传值和另外两者的唯一区别就是第一条汇编指令。其中传值用的是汇编指令mov,而传引用和传指针用的都是汇编指令lea
  然后我搜了下,mov是把内容复制到寄存器eax,而lea是把地址复制到寄存器里。所以这里传值是把变量test_arg的内容复制到寄存器,而后两者是把变量test_arg的地址复制到寄存器。而内容复制一般复制量都比地址复制要大,这也就造成了效率上的差距。且传值修改的是复制的内容,所以实参不会受影响;但后两者修改的是传入指针里的内容,这两个指针(传参和实参指针)指向的内容是一致的,所以实参会收到影响。

  • 所以总结下,函数传参的流程如下:
  1. 执行leamov指令将内容或指针拷贝到寄存器上。
  2. 执行push指令把寄存器里的内容push进栈。
  3. 执行call指令调用函数。
  4. 执行add指令确保堆栈平衡,相当于执行pop操作把前面push的内容弹出。而add的值跟参数个数有关(之前push的值)。

三、体会

  随着和C/C++打交道的时间越来越长,我探索的内容也越发深入、复杂。但是当真正理解了之前疑惑的内容,说实话还是很开心的。
  另外吐槽下csdn上鱼龙混杂,发的大部分都是很基础没有营养的东西,或者不知道哪抄的错误百出的内容,当然也有很多大佬的内容让我受益匪浅(深表感谢orz),现在我搜个东西都得“发掘”半天。但是从某种意义上来讲我是有一点开心的,这说明我至少已经算入门了嘛XD

以上是关于C/C++学习记录:深入理解三种传参方式的主要内容,如果未能解决你的问题,请参考以下文章

ajax的三种传参方式

三种传参方式

vue三种传参方法

vue2 route包含的信息和router使用的详细介绍 vue3 useRouter和useRoute 使用以及三种传参方式

Vue 之 路由常用的几种传参方式

JS有哪几种传参方式