
C指针之指针与函数传参
发布日期:2021-05-07 15:11:48
浏览次数:21
分类:精选文章
本文共 3013 字,大约阅读时间需要 10 分钟。
指针与函数传参
普通变量作为函数形参
- 函数传参时,普通变量作为参数时,形参和实参名字可以相同也可以不同,实际上都是用实参来替代相对应的形参的。
- 在子函数内部,形参的值等于实参。原因是函数调用时把实参的值赋值给了形参。
- 这就是很多书上写的“传值调用”(相当于实参做右值,形参做左值)
/ &a和&b不同,说明a和b不是同一个变量(在内存中a和b是独立的2个内存空间)// 但是a和b是有关联的,实际上b是a赋值得到的。void func1(int b){ // 在函数内部,形参b的值等于实参a printf("b = %d.\n", b); printf("in func1, &b = %p.\n", &b);}int main(void){ int a = 4; printf("&a = %p.\n", &a); func1(a); return 0;}
数组作为函数形参
- 函数名作为形参传参时,实际传递是不是整个数组,而是数组的首元素的首地址(也就是整个数组的首地址。因为传参时是传值,所以这两个没区别)。
- 所以在子函数内部,传进来的数组名就等于是一个指向数组首元素首地址的指针。所以sizeof得到的是4.
- 在子函数内传参得到的数组首元素首地址,和外面得到的数组首元素首地址的值是相同的。
- 很多人把这种特性叫做“传址调用”(所谓的传址调用就是调用子函数时传了地址(也就是指针),此时可以通过传进去的地址来访问实参。)
- 数组作为函数形参时,[ ]里的数字是可有可无的。
- 为什么?因为数组名做形参传递的实际只是个指针,根本没有数组长度这个信息。
void func2(int a[]){ printf("sizeof(a) = %d.\n", sizeof(a)); printf("in func2, a = %p.\n", a);}int main(void){ int a[5]; printf("a = %p.\n", a); func2(a); return 0;}
指针作为函数形参
- 只有一句话:和数组作为函数形参是一样的.
- 这就好像指针方式访问数组元素和数组方式访问数组元素的结果一样是一样的。
void func3(int *a){ printf("sizeof(a) = %d.\n", sizeof(a)); printf("in func2, a = %p.\n", a);}int main(void){ int a[5]; printf("a = %p.\n", a); func3(a); return 0;}
结构体变量作为函数形参
- 结构体变量作为函数形参的时候,实际上和普通变量(类似于int之类的)传参时表现是一模一样的。
- 所以说结构体变量其实也是普通变量而已。
- 因为结构体一般都很大,所以如果直接用结构体变量进行传参,那么函数调用效率就会很低。
- 因为在函数传参的时候需要将实参赋值给形参,所以当传参的变量越大调用效率就会越低
- 怎么解决?思路只有一个那就是不要传变量了,改传变量的指针(地址)进去。
- 结构体因为自身太大,所以传参应该用指针来传(但是可以自己决定,你非要传结构体变量过去C语言也是允许的,只是效率低了);回想一下数组,为什么C语言设计的时候数组传参默认是传的数组首元素首地址而不是整个数组?
struct A{ char a; // 结构体变量对齐问题 int b; // 因为要对齐存放,所以大小是8};void func4(struct A a1){ printf("sizeof(a1) = %d.\n", sizeof(a1)); printf("&a1 = %p.\n", &a1); printf("a1.b = %d.\n", a1.b);}int main(void){ struct A a = { .a = 4, .b = 5555, }; printf("sizeof(a) = %d.\n", sizeof(a)); printf("&a = %p.\n", &a); printf("a.b = %d.\n", a.b); func4(a); return 0;}
void func5(struct A *a1){ printf("sizeof(a1) = %d.\n", sizeof(a1)); // 4 printf("sizeof(*a1) = %d.\n", sizeof(*a1)); // 8 printf("&a1 = %p.\n", &a1); // 二重指针 printf("a1 = %p.\n", a1); printf("a1->b = %d.\n", a1->b);}int main(void){ struct A a = { .a = 4, .b = 5555, }; printf("sizeof(a) = %d.\n", sizeof(a)); // 4 printf("&a = %p.\n", &a); printf("a.b = %d.\n", a.b); func5(&a); return 0;}
传值调用与传址调用
- 传值调用描述的是这样一种现象:
- x和y作为实参,自己并没有真身进入swap1函数内部,而只是拷贝了一份自己的副本(副本具有和自己一样的值,但是是不同的变量)进入子函数swap1
- 然后我们在子函数swap1中交换的实际是副本而不是x、y真身。所以在swap1内部确实是交换了,但是到外部的x和y根本没有受影响。
- 在swap2中x和y真的被改变了(但是x和y真身还是没有进入swap2函数内,而是swap2函数内部跑出来把外面的x和y真身改了)。
- 实际上实参x和y永远无法真身进入子函数内部(进去的只能是一份拷贝),但是在swap2我们把x和y的地址传进去给子函数了,于是乎在子函数内可以通过指针解引用方式从函数内部访问到外部的x和y真身,从而改变x和y。
- 结论:这个世界上根本没有传值和传址这两种方式,C语言本身函数调用时一直是传值的,只不过传的值可以是变量名,也可以是变量的指针。
void swap1(int a, int b){ int tmp; tmp = a; a = b; b = tmp; printf("in swap1, a = %d, b = %d.\n", a, b);}void swap2(int *a, int *b){ int tmp; tmp = *a; *a = *b; *b = tmp; printf("in swap1, *a = %d, *b = %d.\n", *a, *b);}int main(void){ int x = 3, y = 5; swap2(&x, &y); printf("x = %d, y = %d.\n", x, y); // 交换成功 /* int x = 3, y = 5; swap1(x, y); printf("x = %d, y = %d.\n", x, y); // x=3,y=5,交换失败*/ return 0;}
发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年03月26日 00时16分49秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
工作半年的思考
2021-05-09
不可思议的纯 CSS 滚动进度条效果
2021-05-09
【CSS进阶】伪元素的妙用--单标签之美
2021-05-09
惊闻NBC在奥运后放弃使用Silverlight
2021-05-09
IE下尚未实现错误的原因
2021-05-09
创建自己的Docker基础镜像
2021-05-09
HTTP 协议图解
2021-05-09
Python 简明教程 --- 20,Python 类中的属性与方法
2021-05-09
KNN 算法-理论篇-如何给电影进行分类
2021-05-09
Spring Cloud第九篇 | 分布式服务跟踪Sleuth
2021-05-09
CODING 敏捷实战系列课第三讲:可视化业务分析
2021-05-09
使用 CODING DevOps 全自动部署 Hexo 到 K8S 集群
2021-05-09
工作动态尽在掌握 - 使用 CODING 度量团队效能
2021-05-09
CODING DevOps 深度解析系列第二课报名倒计时!
2021-05-09
CODING DevOps 线下沙龙回顾二:SDK 测试最佳实践
2021-05-09
数据结构第八节(图(下))
2021-05-09
基于Mustache实现sql拼接
2021-05-09
POJ 2260 Error Correction 模拟 贪心 简单题
2021-05-09
CSUOJ Water Drinking
2021-05-09
Spring MVC+javamail实现邮件发送
2021-05-09