因为一直对这几种函数参数的传递方式理解的不是很透彻,花了一段时间仔细捋清了他们之间的区别。这个问题也是编程初级阶段会经常遇到的问题,也是有可能在面试中遇到的基本问题,在此进行了简单的总结一下,一是加深自己的理解,二是希望帮助遇到同样问题的同学,希望能帮你们快速透彻的理解他们。

主要以实现交换两个整形值(老生常谈的话题了)为载体进行透彻的说明,我们的主要方法就是利用最基本的输入输出功能来看函数执行前和执行后,参数地址和值的变化来看函数“做了什么事情”。

针对每一个不同的函数,主要从展现了如下几个方面:(1)函数开始执行前的参数的状态,包括参数的地址和值(2)函数实现了什么样的操作(3)函数执行后的参数的状态。最后我们简单的总结了这5个函数为什么有的能实现目的;而有的不能实现目的,进一步解释了这些不能实现目的函数他们到底做了哪些事情(通过前后参数状态的变化来反映)。

在main函数中,我们首先显示出实参的地址,当将实参传入到函数中,我们显示被调函数中变量的地址。只要被调函数中变量地址和实参的地址一样,我们认为是直接操作变量而不是操作变量的”副本“;如果被调函数中变量的地址与实参地址不同,则认为是对实参进行了一次拷贝,即新建了一个实参的”副本“,这个”副本“的值和实参值一样,被调函数在后续的操作中都是对这个”副本“进行操作的,而副本的改变和原实参无任何关系。当被调函数结束后,该副本做为局部变量而结束生命周期。

首先看一下5个常见交换值的函数(有的能实现,有的不能实现交换目的)。依据上述原理,如果交换函数中是对真正实参(数据或指针)的操作,则认为能实现交换目的;而如果只是对实参副本进行交换操作,则认为不能达到交换目的。

本文未从理论上进行过多的论述,需要的同学可以自行搜索一下相关资料。

[cpp] view plaincopy
  1. <span style="font-family:SimSun;">//交换两个数字的值,由于函数中较多的使用了cout来显示相关的值,所以使原本简单的函数看起来很长</span><span style="font-family:SimHei;">。</span>
  2. void change0(int a1,int a2)
  3. {
  4. //这个函数不能实现目的
  5. cout<<"change0 执行前:"<<endl;
  6. cout<<"&a1="<<&a1<<",&a2="<<&a2<<endl;
  7. cout<<"a1="<<a1<<",a2="<<a2<<endl;
  8. int temp=a1;
  9. a1=a2;
  10. a2=temp;
  11. cout<<"change0 执行后:"<<endl;
  12. cout<<"&a1="<<&a1<<",&a2="<<&a2<<endl;
  13. cout<<"a1="<<a1<<",a2="<<a2<<endl;
  14. }
[cpp] view plaincopy
  1. void change1(int *a1,int *a2)
  2. {
  3. //能实现目的
  4. cout<<"change1 执行前:"<<endl;
  5. cout<<"&a1="<<&a1<<",&a2="<<&a2<<endl;
  6. cout<<"*a1="<<*a1<<",*a2="<<*a2<<endl;
  7. cout<<"a1="<<a1<<",a2="<<a2<<endl;
  8. int temp;
  9. temp=*a1;
  10. *a1=*a2;
  11. *a2=temp;
  12. cout<<"change1 执行后:"<<endl;
  13. cout<<"&a1="<<&a1<<",&a2="<<&a2<<endl;
  14. cout<<"*a1="<<*a1<<",*a2="<<*a2<<endl;
  15. cout<<"a1="<<a1<<",a2="<<a2<<endl;
  16. }
  17. void change2(int *a1,int *a2)
  18. {
  19. //这个函数不能实现目的
  20. cout<<"change2 执行前:"<<endl;
  21. cout<<"&a1="<<&a1<<",&a2="<<&a2<<endl;
  22. cout<<"*a1="<<*a1<<",*a2="<<*a2<<endl;
  23. cout<<"a1="<<a1<<",a2="<<a2<<endl;
  24. int *temp=a1;
  25. a1=a2;
  26. a2=temp;
  27. cout<<"change2 执行后:"<<endl;
  28. cout<<"&a1="<<&a1<<",&a2="<<&a2<<endl;
  29. cout<<"*a1="<<*a1<<",*a2="<<*a2<<endl;
  30. cout<<"a1="<<a1<<",a2="<<a2<<endl;
  31. }
  32. void change3(int &a,int &b)
  33. {
  34. cout<<"change3 执行前:"<<endl;
  35. cout<<"a="<<a<<",b="<<b<<endl;
  36. cout<<"&a="<<&a<<",&b="<<&b<<endl;
  37. int temp=a;
  38. a=b;
  39. b=temp;
  40. cout<<"change3 执行后:"<<endl;
  41. cout<<"a="<<a<<",b="<<b<<endl;
  42. cout<<"&a="<<&a<<",&b="<<&b<<endl;
  43. }
  44. void change4(int **a,int **b)
  45. {
  46. cout<<"change4 执行前:"<<endl;
  47. cout<<"&a="<<&a<<",&b="<<&b<<endl;
  48. cout<<"a="<<a<<",b="<<b<<endl;
  49. cout<<"*a="<<*a<<",*b="<<*b<<endl;
  50. cout<<"**a="<<**a<<",**b="<<**b<<endl;
  51. int **temp = new int *;
  52. *temp = *a;
  53. *a = *b;
  54. *b = *temp;
  55. cout<<endl;
  56. cout<<"change4 执行后:"<<endl;
  57. cout<<"&a="<<&a<<",&b="<<&b<<endl;
  58. cout<<"a="<<a<<",b="<<b<<endl;
  59. cout<<"*a="<<*a<<",*b="<<*b<<endl;
  60. cout<<"**a="<<**a<<",**b="<<**b<<endl;
  61. }

change0不能实现目的。简单的说就是值传参只是对实参的副本进行了操作,并不能对实参本身进行操作。这点会通过下面的例子体现出来。

change1能实现目的,但要注意,这种改变是接改变m和n 的值,而pm和pn的指向未改变(与change4正好相反)。从图示中能清楚的看出change1中实现的操作。图中每个圆圈或方块代表一个变量,上方为自己实际的内存地址,其中的内容为他的值。从图中可以看出,change1执行前,生成了两个实参的副本(左侧黄色部分),注意副本的内存地址和实参的不一样的(不然怎么叫重新生成的),他们的值为各自对应实参的值。change1实际上的操作为将变量m和n中的值进行互换,而两个实参的副本还是指向原来的位置(即值不变)。chang1 执行后如图底部所示,指针pm和pn指向的位置不变,但该位置中存储的内容发生了变化。

change2不能实现交换的目的,从图中我们可以看书,change2中完成的操作只是将两个实参副本的值互换了,即只把这俩副本指向的位置改变了(当然,在change2执行结束后他们将消亡),而我们的目的是想改变pm和pn的指向,或互换m和n的值。change2不能实现目的的原因和change0的原因相同,都只是改变了副本的值,而真正的实参没进行任何操作。所以仍各自保持原来的值不变。

change3能实现,他是通过引用的方式实现传递参数的。即直接对传入参数进行操作,而不是对生成的副本进行操作。这样在change3中对参数的任何操作都能直接对参数产生影响,如图所示,当change3执行完毕后,变量m和n的值已经改变了。

change4是用双重指针进行传参,他是通过改变指针pm和pn的指向来实现的,而实际上m和n的值都未改变。但从图中可以看出,change4执行后,ppm和ppn的指向位置未改变,而指向位置的内容却发生了改变,即执行前后ppm始终指向pm,ppn始终指向pn,只不过是执行后pm和pn的指向发了改变。

总结:

利用判断被调函数是对实参操作还是对实参副本操作来判断一个交换函数是否真能实现交换操作;

利用显示变量的内存地址来判断是“实参”还是“实参副本”。

阅读原文

转载于:https://www.cnblogs.com/mayuko/p/4567545.html

图解C/C++中函数参数的值传递、指针传递与引用传递相关推荐

  1. 【C语言】函数参数试探:传指针与传引用——以队列为例

    首先,实现一个简单的队列: #define Maxsize 100 #define ElemType int//队列 typedef struct Queue{ElemType data[Maxsiz ...

  2. C++中函数参数的默认值

    文章目录 1 C++中函数参数的默认值 1.1 C++中函数参数的默认值 1.2 函数默认参数的规则 2 函数占位参数 2.1 函数占位参数 2.2 函数占位参数的意义 1 C++中函数参数的默认值 ...

  3. C++ 笔记(13)— 函数(函数声明、函数定义、函数调用[传值、指针、引用]、函数参数默认值、函数重载)

    每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数. 1. 函数声明 函数声明告诉编译器函数的名称.返回类型和参数.函数声明包括以下几个部分: ret ...

  4. python函数内部返回的值_Python中函数的返回值示例浅析

    前言: 前面我们介绍了简单的介绍了函数和函数的参数,今天我们来说一下Python中函数的返回值. 函数的返回值:函数运算的结果,需要进一步的操作时,给一个返回值return用来返回函数的结果,如果没有 ...

  5. python函数返回值_Python中函数的返回值示例浅析

    前言: 前面我们介绍了简单的介绍了函数和函数的参数,今天我们来说一下Python中函数的返回值. 函数的返回值:函数运算的结果,需要进一步的操作时,给一个返回值return用来返回函数的结果,如果没有 ...

  6. 决定c++语言中函数的返回值类型的是,全国2009年10月高等教育自学考试C++程序设计试题及部分参考答案...

    全国2009年10月高等教育自学考试 C++程序设计试题 课程代码:04737 一.单项选择题(本大题共20小题,每小题1分,共20分) 在每小题列出的四个备选项中只有一个是符合题目要求的,请将其代码 ...

  7. python函数参数的引用传递_Python初学者笔记(八):函数参数的值传递和引用传递...

    函数参数的值传递和引用传递 像我们最熟悉的 C 语言,再写函数参数的同时需要规定函数参数到底是值传递还是引用传递.引用传递导致该参数在函数内的修改出了函数之外也会生效,值传递所有的修改都不会体现到函数 ...

  8. ES6-ES11-第一部分-let、const、解构赋值、模板字符串、简化对象写法、箭头函数、函数参数默认值、rest 参数、扩展运算符、Symbol、迭代器、生成器、Promise、Set、Map

    根据视频进行整理 [https://www.bilibili.com/video/BV1uK411H7on?p=1] 视频资源(百度网盘): 链接:[https://pan.baidu.com/s/1 ...

  9. html绑定带有形参的函数,Python中函数参数类型和参数绑定

    参数类型 Python函数的参数类型一共有五种,分别是: POSITIONAL_OR_KEYWORD(位置参数或关键字参数) VAR_POSITIONAL(可变参数) KEYWORD_ONLY(关键字 ...

最新文章

  1. Linux 监视磁盘空间和使用情况
  2. 去月球“挖土”咯!今天发射的嫦娥五号实现四个“首次”,下个月将样品带回地球...
  3. C++继承的基本语法
  4. 友盟分享成功之后没有提示信息的解决
  5. oracle insert 当前时间_Oracle知识点总结
  6. 每日一题(46)—— volatile
  7. [转载] 用Java语言实现对十六进制字符串异或运算
  8. csdn博客markdown 如何输入上下标(如平方指数等)
  9. JVM夺命连环10问
  10. list lt map gt java_利用Set 对Listlt;Maplt;String,Objectgt;gt; 中的map对象中某一个属性去重...
  11. linux自带的cpu监测工具,Linux CPU实时系统监控工具mpstat
  12. 现代通信原理3.2:线性系统的时域与频域特性
  13. 教孩子学编程_教孩子编程的10种工具
  14. Flash Builder4.7安装
  15. 安卓上的测试软件有哪些,手机硬件检测软件有哪些
  16. Unity插入视频音频
  17. 用Auto.js批量删除空间说说
  18. 所有人体胸部和下半身各部位的英语单词
  19. 前端.什么是冒泡和阻止冒泡的原因和方法
  20. Linux qt教程 qt入门(一)

热门文章

  1. 基于可靠消息方案的分布式事务(四):接入Lottor服务
  2. [20180102]11g的V$SORT_USAGE视图.txt
  3. textarea标签中的换行与空格问题
  4. python: 多线程实现的两种方式及让多条命令并发执行
  5. Django资源大全
  6. 使用 SQL 语句从数据库一个表中随机获取一些数据
  7. python爬取历史上的今天数据并展示。
  8. autodesk powerinspect ultimate 2021中文版
  9. DataNumen RAR Repair中文版
  10. Java类文件结构详解