c++中指针作为函数参数的详细理解
在C语言中,函数的参数不仅可以是整数、小数、字符等具体的数据,还可以是指向它们的指针。用指针变量作函数参数可以将函数外部的地址传递到函数内部,使得在函数内部可以操作函数外部的数据,并且这些数据不会随着函数的结束而被销毁。
像数组、字符串、动态分配的内存等都是一系列数据的集合,没有办法通过一个参数全部传入函数内部,只能传递它们的指针,在函数内部通过指针来影响这些数据集合。
有的时候,对于整数、小数、字符等基本类型数据的操作也必须要借助指针,一个典型的例子就是交换两个变量的值。
有些初学者可能会使用下面的方法来交换两个变量的值:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h> void swap( int a, int b){ int temp; //临时变量 temp = a; a = b; b = temp; } int main(){ int a = 66, b = 99; swap(a, b); printf ( "a = %d, b = %d\n" , a, b); return 0; } |
运行结果:
a = 66, b = 99
从结果可以看出,a、b 的值并没有发生改变,交换失败。这是因为 swap() 函数内部的 a、b 和 main() 函数内部的 a、b 是不同的变量,占用不同的内存,它们除了名字一样,没有其他任何关系,swap() 交换的是它内部 a、b 的值,不会影响它外部(main() 内部) a、b 的值。
改用指针变量作参数后就很容易解决上面的问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h> void swap( int *p1, int *p2){ int temp; //临时变量 temp = *p1; *p1 = *p2; *p2 = temp; } int main(){ int a = 66, b = 99; swap(&a, &b); printf ( "a = %d, b = %d\n" , a, b); return 0; } |
运行结果:
a = 99, b = 66
调用 swap() 函数时,将变量 a、b 的地址分别赋值给 p1、p2,这样 *p1、*p2 代表的就是变量 a、b 本身,交换 *p1、*p2 的值也就是交换 a、b 的值。函数运行结束后虽然会将 p1、p2 销毁,但它对外部 a、b 造成的影响是“持久化”的,不会随着函数的结束而“恢复原样”。
需要注意的是临时变量 temp,它的作用特别重要,因为执行*p1 = *p2;语句后 a 的值会被 b 的值覆盖,如果不先将 a 的值保存起来以后就找不到了。
用数组作函数参数
数组是一系列数据的集合,无法通过参数将它们一次性传递到函数内部,如果希望在函数内部操作数组,必须传递数组指针。下面的例子定义了一个函数 max(),用来查找数组中值最大的元素:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <stdio.h> int max( int *intArr, int len){ int i, maxValue = intArr[0]; //假设第0个元素是最大值 for (i=1; i<len; i++){ if (maxValue < intArr[i]){ maxValue = intArr[i]; } } return maxValue; } int main(){ int nums[6], i, maxValue; int len = sizeof (nums)/ sizeof ( int ); //读取用户输入的数据并赋值给数组元素 for (i=0; i<len; i++){ scanf ( "%d" , nums+i); } printf ( "Max value is %d!\n" , max(nums, len)); return 0; } |
运行结果:
12 55 30 8 93 27↙
Max value is 93!
参数 intArr 仅仅是一个数组指针,在函数内部无法通过这个指针获得数组长度,必须将数组长度作为函数参数传递到函数内部。数组 nums
的每个元素都是整数,scanf() 在读取用户输入的整数时,要求给出存储它的内存的地址,nums+i就是第 i 个数组元素的地址。
用数组做函数参数时,参数也能够以“真正”的数组形式给出。例如对于上面的 max() 函数,它的参数可以写成下面的形式:
1
2
3
4
5
6
7
8
9
|
int max( int intArr[6], int len){ int i, maxValue = intArr[0]; //假设第0个元素是最大值 for (i=1; i<len; i++){ if (maxValue < intArr[i]){ maxValue = intArr[i]; } } return maxValue; } |
int intArr[6]好像定义了一个拥有 6 个元素的数组,调用 max()
时可以将数组的所有元素“一股脑”传递进来。
读者也可以省略数组长度,把形参简写为下面的形式:
1
2
3
4
5
6
7
8
9
|
int max( int intArr[], int len){ int i, maxValue = intArr[0]; //假设第0个元素是最大值 for (i=1; i<len; i++){ if (maxValue < intArr[i]){ maxValue = intArr[i]; } } return maxValue; } |
int
intArr[]虽然定义了一个数组,但没有指定数组长度,好像可以接受任意长度的数组。
实际上这两种形式的数组定义都是假象,不管是int intArr[6]还是int
intArr[]都不会创建一个数组出来,编译器也不会为它们分配内存,实际的数组是不存在的,它们最终还是会转换为int
*intArr这样的指针。这就意味着,两种形式都不能将数组的所有元素“一股脑”传递进来,大家还得规规矩矩使用数组指针。
int intArr[6]这种形式只能说明函数期望用户传递的数组有 6
个元素,并不意味着数组只能有 6 个元素,真正传递的数组可以有少于或多于 6 个的元素。
需要强调的是,不管使用哪种方式传递数组,都不能在函数内部求得数组长度,因为 intArr
仅仅是一个指针,而不是真正的数组,所以必须要额外增加一个参数来传递数组长度。
C语言为什么不允许直接传递数组的所有元素,而必须传递数组指针呢?
参数的传递本质上是一次赋值的过程,赋值就是对内存进行拷贝。所谓内存拷贝,是指将一块内存上的数据复制到另一块内存上。
对于像 int、float、char
等基本类型的数据,它们占用的内存往往只有几个字节,对它们进行内存拷贝非常快速。而数组是一系列数据的集合,数据的数量没有限制,可能很少,也可能成千上万,对它们进行内存拷贝有可能是一个漫长的过程,会严重拖慢程序的效率,为了防止技艺不佳的程序员写出低效的代码,C语言没有从语法上支持数据集合的直接赋值。
除了C语言,C++、Java、Python 等其它语言也禁止对大块内存进行拷贝,在底层都使用类似指针的方式来实现。
c++中指针作为函数参数的详细理解的更多相关文章
- C++ 二维数组(双重指针作为函数参数)
本文的学习内容参考:http://blog.csdn.net/yunyun1886358/article/details/5659851 http://blog.csdn.net/xudongdong ...
- Day8 函数指针做函数参数
课堂笔记 课程回顾 多态 virtual关键字 纯虚函数 virtual func() = 0; 提前布局vptr指针 面向接口编程 延迟绑定 多态的析构函数的虚函数. ...
- go语言基础之数组指针做函数参数
1.数组指针做函数参数 示例: package main //必须有个main包 import "fmt" //p指向实现数组a,它是指向数组,它是数组指针 //*p代表指针所指向 ...
- go语言基础之指针做函数参数用地址传递
1.指针做函数参数 示例: package main //必须有个main包 import "fmt" func swap(p1, p2 *int) { *p1, *p2 = *p ...
- go语言基础之指针做函数参数
1.指针做函数参数 示例: package main //必须有个main包 import "fmt" func swap(a, b int) { a, b = b, a fmt. ...
- [编程] C语言结构体指针作为函数参数
结构体指针作为函数参数:结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针.如果结构体成员较多,尤其是成员为数组时,传送的时间和空间 ...
- 在实验中观察指针——C++ 函数参数的压栈顺序
前言 好久没写东西了,突发奇想,写写函数参数的压栈顺序 先看看这个问题 https://q.cnblogs.com/q/137133/ 然后看我简化的代码,猜输出结果是多少? #include< ...
- 我的c++学习(12)指针作为函数参数
◆ 引用调用与指针传值调用C++中函数的参数的基本使用方法是传值.为了弥补单纯传值的不足,以引用作为函数的参数,从逻辑上讲引用是别名,在函数中对参数的操作,就是对实参的操作,而在物理上是传实参的地址. ...
- C++中,引用作为函数参数
引用作为函数参数 C++之所以增加引用类型, 主要是把它作为函数参数,以扩充函数传递数据的功能. ———————————————————— c++,函数传参:(1)将变量名作为实参和形参.这时传给形参 ...
随机推荐
- 尽管以C++为基础,但 Java 是一种更纯粹的面向对象程序设计语言
“尽管以C++为基础,但 Java 是一种更纯粹的面向对象程序设计语言”. 无论C++还是Java 都属于杂合语言.但在 Java 中,设计者觉得这种杂合并不象在 C++里那么重要.杂合语言 允许采用 ...
- 《linux系统及其编程》实验课记录(一)
实验 1:登录和使用基本的 Linux 命令 实验环境: 安装了 Red Hat Enterprise Linux 6.0 可运行系统,并且是成功验证系统. 有另外一个无特权用户 student,密码 ...
- (转)Unity笔记之编辑器(Foldout、HelpBox、InspectorTitlebar、Slider、MinMaxSlid ...
1. Foldout.HelpBox 折叠菜单,大家都知道,不具体解释了,直接代码.因为折叠菜单中必然是有内容才能看到效果,所以顺带把HelpBox(提示框)也说了. [code]csharpcode ...
- 为什么要使用自增ID作为主键
1.从业务上来说 在设计数据库时不需要费尽心思去考虑设置哪个字段为主键.然后是这些字段只是理论上是唯一的,例如使用图书编号为主键,这个图书编号只是理论上来说是唯一的,但实践中可能会出现重复的 情况.所 ...
- LINUX下搭建JAVA的开发环境
LINUX下搭建JAVA的开发环境 (2009-07-13 10:04:13) 下面就将Linux下JAVA开发环境的搭建详细道来: 1.Linux下JDK的安装 至于下载JDK的二进制可执行 ...
- Python调用外部程序
通过os.system和subprocess.call()函数调用其他程序 预备知识:cmd中打开和关闭程序 cmd中打开程序 a.打开系统自带程序 系统自带的程序的路径一般都已加入环境变量之中,只需 ...
- cxGrid 显示行号及行号列列名
cxGrid默认不显示行号,但是可以通过cxGrid1DBTableView1CustomDrawIndicatorCell事件来重绘行号 选中cxGrid1DBTableView1,在OnCusto ...
- Dart SDK在Linux上的下载及配置
Dart SDK在Linux上的下载及配置 首先建议想要学习Dart的小伙伴选择Linux开发环境比较好,具体原因就不多说了.如果想要尝试在Window或Mac上安装Dart SDK的话可以参考Ins ...
- MyEclipse中手工添加dtd支持
1.先下载好相应的dtd文件,如struts-2.3.dtd 2.打开MyEclipse,Window->Preferences 在搜索框中输入"XML Catalog" 3 ...
- hdu 4512 吉哥系列故事——完美队形I【LCIS经典应用】
链接: http://acm.hdu.edu.cn/showproblem.php?pid=4512 http://acm.hust.edu.cn/vjudge/contest/view.action ...