PS:首先理解形参   实参概念。形参是在函数定义的括号内定义的专用变量,它们的目的是保存按实参传递给它们的信息,实参被列在函数调用语句的括号内。

int func(int x)//x是形参
{return x*x;
}
int main(void)
{int a = 3;func(a);//a是实参return 0;
}

上面的代码中,x是形参,a是实参。形参x是实参a的一个拷贝

1.C++    按值传递

采用按值传递的方式,当信息被传递给一个函数时,这意味着形参接收的是传递给它的值的副本。如果形参的值在函数内部被改变,那么它对原始实参是没有影响的。

#include <iostream>
using namespace std;
// 函数声明
void changeMe(int aValue);
int main()
{int number = 12;// 输出number的值               此处输出为  12cout << "In main number is " << number << endl;//调用changeMe()函数,将number中的值作为参数传递changeMe(number);                   //此处调用函数内部有一个输出为   0// 调用函数之后,再次输出number的值          此处输出为  12cout << "Back in main again, number is still " << number << endl;return 0;
}
void changeMe(int myValue)
{//改变 myValue 的值为 0myValue = 0;//输出 myValue的值             此处输出为  0cout << "In changeMe, the value has been changed to " << myValue << endl;
}

程序输出结果:

In main number is 12
In changeMe, the value has been changed to 0
Back in main again, number is still 12

当函数原型列出变量名称和数据类型时,它使用的名称只是虚拟名称,编译器实际上并不使用它们,并且不必在函数头中使用一致的名称。第 5 行中的 changeMe 函数原型和第 19 行中的 changeMe 函数头都指定该函数具有一个 int 形参,但它们使用了不同的名称。
并且,即使在 changeMe 函数中形参变量 myValue 已更改,实参 number 也不会被修改。这是因为 myValue 变量只包含了 number 变量的副本。只是这个副本被改变,而不是原来的。changeMe 函数无法访问原始实参。图 1 说明了形参变量在内存中的存储位置与原始实参的存储位置是分开的。

2.C++    指针传递

传指针就是把实参的地址传递给函数。传指针可以修改实参的值,在C++里也不会存在调用对象的拷贝构造函数的问题, 传指针的效率比传值要高。所以,如果需要修改实参的值,就不能传值,而需要传指针等。但是,传指针比传值复杂,指针计算一旦移动出了正常范围,会造成程序的非法访问等。

void func(int *x)//func采用了传指针的形式
{*x = *x+1;printf("*x=%d\n", *x);
}
int main(void)
{int a = 0;func(&a);//把实参a的地址传递给了函数funcprintf("a=%d\n", a);return 0;
}

分析:传指针可以修改实参的值。根据指针的定义,*x就是a,所以,*x=*x+1,即为a = a+1,所以上面的代码输出结果为:
*x=1
a=1

C/C++ 禁止在函数调用时直接传递数组的内容,而是强制传递数组指针

而对于结构体和对象没有这种限制,调用函数时既可以传递指针,也可以直接传递内容;为了提高效率,我曾建议传递指针,这样做在大部分情况下并没有什么不妥,读者可以点击《C语言结构体指针》进行回顾。

在 C++ 中,我们有了一种比指针更加便捷的传递聚合类型数据的方式,那就是引用(Reference)

3.C++   引用传递

引用介绍参照博客https://blog.csdn.net/m0_37957160/article/details/104705220

前面讲过,实参通常是通过值传递给函数的,这意味着形参接收的只是发送给它们的值的副本,它们存储在函数的本地内存中。对形参值进行的任何更改都不会影响原始实参的值。然而,有时候可能会希望一个函数能够改变正在调用中的函数(即调用它的函数)中的一个值,这可以通过引用传递的方式来完成。

我们知道,变量是可以保存数据的内存位置的名称。当使用变量时,实际上就是访问存储在分配给它的内存位置的数据。引用变量是变量的另一个别名,它没有自己的存储数据的内存位置,它访问的是另一个变量的内存位置。对引用变量作出的任何更改,实际上都是对它所引用的变量内存位置中存储数据的更改。

当使用引用变量作为形参时,它将变为实参列表中相应变量的别名,对形参进行的任何更改都将真正更改正在调用它的函数中的变量。当以这种方式将数据传递给形参时,该实参被称为按引用传递。

在定义或声明函数时,我们可以将函数的形参指定为引用的形式,这样在调用函数时就会将实参和形参绑定在一起,让它们都指代同一份数据。如此一来,如果在函数体中修改了形参的数据,那么实参的数据也会被修改,从而拥有“在函数内部影响函数外部数据”的效果。

引用变量的定义方法和常规变量类似,但是其数据类型和名称之间有一个 & 符号。例如,以下函数定义使形参 refVar 成为引用变量:

void doubleNum(int& refVar)
{refVar *= 2;
}

注意,变量 refVar 被称为“对 int 的引用”。
该函数将 refVar 乘以 2,因为 refVar 是引用变量,所以该操作实际上将对作为实参传递给函数的变量执行。
具有引用形参的函数的原型也必须具有 & 符号。与函数头一样,它在数据类型和变量名之间。如果原型中省略了变量名,那么 & 符号将跟在数据类型后面。以下所有 doubleNum 函数的原型都是正确的:

void doubleNum(int &refVar);
void doubleNum(int& refVar);
void doubleNum(int &);
void doubleNum(int&);

注意,& 符号必须出现在使用引用变量作为形参的任何函数的原型和函数头中。它不会出现在函数调用中。

#include <iostream>
using namespace std;
// Function prototype. The parameter is a reference variable.
void doubleNum(int SrefVar);
int main()
{int value = 4;cout << "In main, value is " << value << endl;      //输出4cout << "Now calling doubleNum..." << endl;doubleNum(value);        cout << "Now back in main, value is "<< value << endl;             //输出4return 0;
}
void doubleNum (int SrefVar)
{refVar *= 2;
}

程序输出结果:

In main, value is 4
Now calling doubleNum...
Now back in main, value is 8

此程序中的形参 refVar “指向”函数 main 中的 value 变量。当程序使用一个引用变量时,它实际上是使用它所引用或指向的变量,(引用变量实际上指向的是被它引用的变量)

当函数的目的是接收调用它的函数存储在变量中的输入值时,使用引用变量作为函数 形参特别有用。另外,引用形参还可以用于必须从函数发回多个值的情形。如果函数将计算并发回单个值,通常认为使用返回值的函数更合适,可以使用 return 语句返回值。

下面程序是对之前程序的修改。它添加了一个函数 getNum,该函数接收用户的输入并将其存储在 userNum 中,但是,形参 userNum 是对 main 的变量 value 的引用,所以这是实际存储输入数据的位置。

#include <iostream>
using namespace std;//Function prototypes
void getNum(int &);
int doubleNum(int);int main()
{int value;// Call getNum to get a number and store it in valuegetNum(value);value = doubleNum(value);                  //调用函数后,将value变为24// Display the resulting numbercout << "That value doubled is " << value << endl;     //最后输出 24return 0;
}
void getNum(int &userNum)
{cout << "Enter a number: ";cin >> userNum;                        //输入12
}
int doubleNum (int number)
{return number *2;
}

程序输出结果:

Enter a number: 12
That value doubled is 24

注意,只有变量才能按引用传递。如果尝试将非变量实参(例如常数、常量或表达式)传递到引用形参中,将导致错误。

如果一个函数有多个形参是引用变量,则必须在原型和函数头中为每个形参使用 & 符号。以下是使用 4 个引用变量形参的函数的原型:

void addThree(int& num1, int& num2, int& num3, int& sum);

以下是函数定义:

void addThree(int& numl, int& num2, int& num3, int& sum)
{cout << "Enter three integer values: ";cin >> num1 >> num2 >> num3;sum = num1 + num2 + num3;
}

但是请注意,addThree 函数只需要一个引用形参 sum,其他 3 个形参可以通过值接收它们的实参,因为在此处的函数中没有改变它们。

提示:应该仅在绝对需要时才使用引用变量。任何时候允许一个函数来改变函数之外的变量,其实都是在创建潜在的调试问题。

文章转自C语言中文网:http://c.biancheng.net/view/2251.html

引用的本质:

在定义或声明函数时,我们可以将函数的形参指定为引用的形式,这样在调用函数时就会将实参和形参绑定在一起,让它们都指代同一份数据。如此一来,如果在函数体中修改了形参的数据,那么实参的数据也会被修改,从而拥有“在函数内部影响函数外部数据”的效果。

总之:传值不能修改实参,且如果是对象,效率较低;传指针能够修改实参,效率较高,但容易出错;传引用能够修改实参,效率较高,而且不易出错。

一个能够展现按引用传参的优势的例子就是交换两个数的值,如下所示:

#include <iostream>
using namespace std;void swap1(int a, int b);
void swap2(int *p1, int *p2);
void swap3(int &r1, int &r2);int main() {int num1, num2;cout << "Input two integers: ";cin >> num1 >> num2;swap1(num1, num2);cout << num1 << " " << num2 << endl;    //输出与输入相同,因为值传递,不能改变原始数据cout << "Input two integers: ";cin >> num1 >> num2;swap2(&num1, &num2);cout << num1 << " " << num2 << endl;    //输出与输入相反,指针传递可以改变原始数据cout << "Input two integers: ";cin >> num1 >> num2;swap3(num1, num2);cout << num1 << " " << num2 << endl;     //输出与输入相反,因为引用传递,可以改变原始数据return 0;
}//直接传递参数内容
void swap1(int a, int b) {int temp = a;a = b;b = temp;
}//传递指针
void swap2(int *p1, int *p2) {int temp = *p1;*p1 = *p2;*p2 = temp;
}//按引用传参
void swap3(int &r1, int &r2) {int temp = r1;r1 = r2;r2 = temp;
}

运行结果:
Input two integers: 12 34↙
12 34
Input two integers: 88 99↙
99 88
Input two integers: 100 200↙
200 100

本例演示了三种交换变量的值的方法:
1) swap1() 直接传递参数的内容,不能达到交换两个数的值的目的。对于 swap1() 来说,a、b 是形参,是作用范围仅限于函数内部的局部变量,它们有自己独立的内存,和 num1、num2 指代的数据不一样。调用函数时分别将 num1、num2 的值传递给 a、b,此后 num1、num2 和 a、b 再无任何关系,在 swap1() 内部修改 a、b 的值不会影响函数外部的 num1、num2,更不会改变 num1、num2 的值。

2) swap2() 传递的是指针,能够达到交换两个数的值的目的。调用函数时,分别将 num1、num2 的指针传递给 p1、p2,此后 p1、p2 指向 a、b 所代表的数据,在函数内部可以通过指针间接地修改 a、b 的值。我们在《C语言指针变量作为函数参数》中也对比过第 1)、2) 中方式的区别。

2) swap3() 是按引用传递,能够达到交换两个数的值的目的。调用函数时,分别将 r1、r2 绑定到 num1、num2 所指代的数据,此后 r1 和 num1、r2 和 num2 就都代表同一份数据了,通过 r1 修改数据后会影响 num1,通过 r2 修改数据后也会影响 num2。

从以上代码的编写中可以发现,按引用传参在使用形式上比指针更加直观。在以后的 C++ 编程中,我鼓励读者大量使用引用,它一般可以代替指针(当然指针在C++中也不可或缺),C++ 标准库也是这样做的。

C++ 函数参数传递:传值,传指针,传引用相关推荐

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

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

  2. C++ 传指针还是引用?

    用C++做了一段时间毕业设计了,突然有一天被困在传指针还是传引用的问题上.后来翻看了<C++ 编程规范>,并结合了项目实际情况,总结出下面结论: 传指针 如果函数保存了地址.例:复制地址给 ...

  3. 简单了解函数的传值调用与传址调用(C语言)

    一.首先需要引入函数中的实际参数与形式参数 实际参数(实参)是指真实传给函数的参数.实参可以是常量.变量.表达式.函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值 ...

  4. C++传指针和引用demo

    #include <stdio.h> #include <iostream> using namespace std;//1.传指针 void trans_pointer(in ...

  5. 【中级软考】函数参数传递传值与传引用的区别(global关键字,函数内定义全局变量)

    传值调用最显著的特征就是被调用的函数内部对形参的修改不影响实参的值. 引用调用是将实参的地址传递给形参,使得形参的地址就是实参的地址. (对于python而言,普通的实参传个变量(或常量)进去就相当于 ...

  6. 函数参数传递、数组指针、二级指针、左值、引用

    数组和指针的区别 数组内地址一定连续,指针则不一定连续. 数组赋值要么初始化就设定,要么循环元素赋值.而指针随时可以赋值(改变指向). 数组的存储空间,不是在静态区就是在栈上.指针位置随意. size ...

  7. C语言中函数参数传递

    C语言中函数参数传递的三种方式 (1)值传递,就是把你的变量的值传递给函数的形式参数,实际就是用变量的值来新生成一个形式参数,因而在函数里对形参的改变不会影响到函数外的变量的值. (2)地址传递,就是 ...

  8. C++中引用,指针,指针的引用,指针的指针

    定义一个指针的三种写法都对:1. int * p;  2. int* p;  3. int *p; 习惯不同而已 定义一个函数指针的三种写法都对:1. int *p(); 2. int * p();  ...

  9. Python函数参数传递:传值还是传引用

    引子 首先来看一个列子: def change(val):val.append(100)val = ['T', 'Z', 'Y'] nums = [0, 1] change(nums) print(n ...

最新文章

  1. svn目录结构 php,svn – php代码组织
  2. python windows错误码
  3. Redis Pub/Sub (Java)
  4. 硅谷顶级VC发声:AI技术公司毛利实在太低,人工和算力成本太高
  5. linux服务器后台运行程序
  6. php pack、unpack、ord 函数使用方法(二进制流接口应用实例)
  7. 知识工程学:一个新的重要研究领域
  8. Eclipse+Web3j开发以太坊应用
  9. php音频添加语音,PHP生成语音
  10. 玩转oracle 11g(47):oracle删除非空表空间
  11. 清华大学:2021元宇宙研究报告
  12. CVPR2020 Oral|场景去遮挡新方法:港中文首次提出自监督新框架,已开源
  13. mysql数据库事务隔离级别是_数据库事务隔离级别-MySQL为例 · Sean
  14. 自学前端真的没有前途吗?
  15. Harmony OS — ProgressBar垂直、水平进度条
  16. 【语音处理】基于matlab GUI音频信号提取分析【含Matlab源码 1738期】
  17. android camera无预览拍照 后台拍照
  18. 关于使用CAD文件预览的使用
  19. 计算机连接交换机配置命令,[计算机]交换机配置命令.doc
  20. 美颜滤镜API算法代码解析

热门文章

  1. mysql 5.7笔记_关于MySql 5.7.29免安装版本的一个笔记
  2. android ecplise 的 项目结构目录
  3. 云时代架构阅读笔记二——一次CPU负载超高的分析
  4. java学习之成员内部类
  5. 生产环境项目问题记录系列(一):一次循环数据库拖垮服务器问题
  6. python 数字循环
  7. Chapter 4 Invitations——4
  8. Linux 准确查找结构体定义位置
  9. linux下出现ping:unknown host www.baidu.com问题时的解决办法——ubuntu下局域网络的配置...
  10. 一分钟了解负载均衡的一切