函数传参的原理和对象初始化的原理完全一致,用实参去初始化形参

函数传参的两种形式

1.按值传参

按值传参时,实参的拷贝用来初始化形参。

int main(int argc, char const *argv[])
{int i=0;paratest(i);cout<<i<<endl;return 0;
}void paratest(int a)
{++a;cout<<__func__<<a<<endl;
}

对paratest函数的形参a做的所有操作都不会影响i的值,上述过程相当于如下过程

int a=i; ++a;

a和i是两个不同的对象,a的变化并不会导致i的变化

传指针参数也是按值拷贝的一种,只不过会改变指针指向的对象,不会改变指针本身

int main(int argc, char const *argv[])
{int i=0;int *pi=&i;paratest(pi);cout<<i<<endl;return 0;
}void paratest(int *pa)
{*pa=10;
}

输出结果显示i的值为10,这是因为将i的指针的副本传入paratest,导致pa也指向i,所以可以通过pa改变i的值,上述过程相当于如下过程

int *pi=&i; int *pa=pi;

第二行代码导致pa和pi都指向i的地址,所以都可以操作i的数据,但是传指针参数实质还是按值传参

2.按引用传参

将函数的形参声明为引用,此时向函数中传参时,传递的是实参本身,而不是实参的拷贝,此时形参与实参绑定,形参只不过是实参的别名

int main(int argc, char const *argv[])
{int i=0;paratest(i);cout<<i<<endl;return 0;
}void paratest(int &ra)
{ra=10;
}

此时对引用形参操作,就是对实参操作

因为传递的是实参本身,并没有发生拷贝,省略了传值过程中的拷贝过程,所以对于大的对象来说,效率更高

而且有些对象不支持拷贝操作,所以传参时,必须使用引用形参

const与函数参数

当非引用或者指针的函数形参有const修饰时,传递const对象和非const对象都是OK的

int main(int argc, char const *argv[])
{int i=0;paratest(i);const int ci=10;paratest(ci);return 0;
}void paratest(const int a)
{cout<<__func__<<a<<endl;
}

上述代码可以编译通过,但是不能在函数中修改a

函数const变量的指针或者引用的形参的初始化和const变量的指针或者引用的初始化规则完全一致,见知识点2,3

在声明一个函数时,尽量使用常量引用来声明形参

int main(int argc, char const *argv[])
{string str="asd";constreferencepara(str);//constreferencepara("asd");return 0;
}void constreferencepara(string &str)
{}

上述代码的第5行必须注释掉,否则会报错

因为一般的引用无法绑定字面值,但是const变量的引用可以,而且const变量的引用也可以绑定一般的变量,所以如果将函数constreferencepara的参数声明为const string &str,那么就不会报错

int main(int argc, char const *argv[])
{string str="asd";constreferencepara(str);constreferencepara("asd");return 0;
}void constreferencepara(const string &str)
{}

此时函数可以传入参数的范围变大,扩展性提高,而且传递引用不需要拷贝对象,效率提高,所以,建议使用const变量的引用作为函数形参

数组形参

当使用值传递的方式传递数组参数时,数组参数会转化为一个指针,指向数组首元素地址,下列三种函数声明都是等价的

void func(int *a);
void func(int a[]);
void func(int a[10]);

第三中声明方式只是期望传入的数组有10个元素,但是本质和前两种完全一样

当分别实现这三个函数时,编译器会报重定义的错误

void func(int *a)
{cout<<"pointer func"<<endl;
}void func(int a[])
{cout<<"array func"<<endl;
}void func(int a[10])
{cout<<"array func that include 10 ele"<<endl;
}

由于向函数中传入数组时,函数接收到的只是数组首元素的指针,如果在函数中需要遍历数组,需要人为指定数组长度

int main(int argc, char const *argv[])
{int a[10]={1,2,3,4,5};int len=sizeof(a)/sizeof(a[0]);//计算数组长度func(a,len);return 0;
}void func(int *a,int len)
{for (int i=0;i<len;++i) {cout<<a[i]<<endl;}
}

这样做不如使用vector简洁,建议使用vector代替数组

传递数组的引用和指针

如果一个函数的形参是数组的引用,那么必须指定数组长度,因为引用要绑定某个具体的数组,不能绑定一个长度不确定的数组

void arrayreferpara(int (&arr)[]);

int main(int argc, char const *argv[])
{int a[10]={1,2,3,4,5};arrayreferpara(a);return 0;
}void arrayreferpara(int (&arr)[10])
{for (int i=0;i<10;++i) {cout<<arr[i]<<endl;}
}

因为使用数组的引用作为形参时,传入的数组参数的个数必须和形参数组的个数一致,所以这种接口的可扩展性不强

函数的形参如果是数组的指针也是同理,数组的长度必须指定,因为数组的指针必须指向一个长度固定的数组

void arrayreferpara(int (*arr)[]);

通过数组的指针遍历数组

int main(int argc, char const *argv[])
{int a[10]={1,2,3,4,5};arrayreferpara(&a);return 0;
}void arrayreferpara(int (*arr)[10])
{for (int *p=arr[0];p!=arr[0]+10;p++) {cout<<*p<<endl;}
}

因为形参是一个数组的指针,所以可以传入一个数组的数组,也就是二维数组,道理和指针形参可以传对应数组相同,下面三个的函数定义完全是一个意思

void arraypointerpara(int (*arr)[10])
{}void arraypointerpara(int arr[10][10])//第一个维度只是个期望值,可以填任意数
{}void arraypointerpara(int arr[][10])
{}

所以会报重定义的错,道理同一维数组

这三种形式中,数组形参的第二个唯独必须确定,因为二维数组依然是一个一维数组,只不过每个元素都是一个一维数组,每个元素必须确定,所以每个元素的维度必须确定

当传入一个二维数组后,数组的指针指向二维数组的第一个一维数组

int main(int argc, char const *argv[])
{int a[2][10]={1,2,3,4,5};int dim=sizeof(a)/sizeof(int)/10;arrayreferpara(a, dim);return 0;
}void arrayreferpara(int arr[][10], int length)
{/*int num=sizeof(arr)/sizeof(int)/10;cout<<num<<endl;*/for (int (*ap)[10]=&arr[0];ap!=&arr[0]+length;ap++) {for (int *p=ap[0];p!=ap[0]+10;++p) {cout<<*p<<endl;}}
}

因为函数的数组形参接收的是数组名,是一个地址,所以不要在函数内对数组形参做sizeof,否则会出警告

提示sizeof将返回数组指针的大小而不是数组的大小

参考:

《C++ Primer》

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

C++知识点7——函数传参相关推荐

  1. x64汇编第三讲,64位调用约定与函数传参.

    目录 x64汇编第三讲,64位调用约定与函数传参. 一丶复习X86传参 二丶x64汇编 2.1汇编详解 x64汇编第三讲,64位调用约定与函数传参. 一丶复习X86传参 在x86下我们汇编的传参如下: ...

  2. C++函数传参int a,int *a,int a,const int a的区别

    C++函数传参int a,int &a,const int &a的区别 # 传参方式                  作用 int a 值传递 无法改变a的值 int *a 地址传递 ...

  3. python如何次传参给线程_python如何给线程中的函数传参?

    1.Process说明 (1)概念 process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建. (2)语法([group [, target [, name [, args [, k ...

  4. Python类三种方法,函数传参,类与实例变量

    1 Python的函数传递: 首先所有的变量都可以理解为内存中一个对象的'引用' a = 1 def func(a):a = 2 func(a) print(a) # 1a = 1 def fun(a ...

  5. C语言指针与函数传参

    指针与函数传参 普通变量与函数形参 数组作为函数形参 结构体变量作为函数形参 传值调用和传址调用 输入型参数和输出型参数 函数的形参和返回值 函数传参中使用const指针 函数如何向外部返回多个值 总 ...

  6. python类与函数编程_Python类三种方法,函数传参,类与实例变量(一)详解

    1 Python的函数传递: 首先所有的变量都可以理解为内存中一个对象的'引用' a = 1 def func(a): a = 2 func(a) print(a) # 1 a = 1 def fun ...

  7. 函数传参string_JavaScript 高阶函数入门浅析

    原文:https://www.freecodecamp.org/news/a-quick-intro-to-higher-order-functions-in-javascript-1a014f89c ...

  8. python简短语法_写出优雅简洁的 python 语法(二)函数传参

    Python中函数传递参数的形式包含多种,可根据情况而定使用哪种方式.基本所有语言都有简介明了的语法去替代繁琐的代码,优雅的写法不仅能提升代码美观,更能提高提高开发效率,让代码更加易读. 位置传递,默 ...

  9. 函数传参string_C/C++的三种函数传值方式及其区别

    C/C++函数传参方式我想很多朋友应该都知道,但是不同传参方式的背后他们的区别是什么我想很多人并不那么清楚.本文就给大家揭露一下各传参方式的区别. 传参方式有这三种:值传递.引用传递.指针传递 大家可 ...

最新文章

  1. 图模型+Bert香不香?完全基于注意力机制的图表征学习模型Graph-Bert
  2. java c s 与b s架构结合使用_Java技术学习笔记:C/S 与B/S 区别
  3. %fplot('Untitled1',[-1,2])画图
  4. ASP.NET Core微服务(二)——【ASP.NET Core Swagger配置】
  5. 一份C++学习资源整理,咬牙切齿地好用。
  6. 在java中使用关键字导入包_java中import关键字的使用方法
  7. (转)Java中equals和==、hashcode的区别
  8. CGImageRef 图片压缩 裁减
  9. 机器学习中的不平衡分类方法(part4)--朴素贝叶斯分类器
  10. 浏览器解析JavaScript的原理
  11. Python机器学习:KNN算法之01KKN基础
  12. asp.net 安装element ui_vue入门003~vue项目引入element并创建一个登录页面
  13. windows10 1909 X64位 精简优化珍藏版
  14. WPS客户端更新日志留着备用
  15. Unreal蓝图入门 节点
  16. 服务器可以装win7或win10系统吗,win10改win7用legacy还是uefi?_网站服务器运行维护,window...
  17. 文档控件Aspose.words for.java 授权须知
  18. android横向卡片式布局,创建卡片式布局  |  Android 开发者  |  Android Developers
  19. 数电实验七:译码显示电路
  20. 3、关于手机型号的前缀翻译表

热门文章

  1. 尹中立:“人造牛市”的结局可能会非常悲惨
  2. MFC中的几个常用类——CWnd
  3. Oracle数据库无法启动解决方法
  4. ubuntu查看硬件信息
  5. SQL Server 2012笔记分享-48:备份时间线
  6. Windows Phone 7 IEnumerableT.Select和SelectMany的区别
  7. Redis常见面试题总结
  8. Win10安装Ubuntu系统
  9. matlab工作路径
  10. java诡异的String.split()方法