函数间参数传递
看清值传递、地址传递与返回值的本质差异
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
函数间参数传递
看清值传递、地址传递与返回值的本质差异
什么是形参和实参
上页说到函数之间通过参数交换数据。但这'参数'二字其实有两种身份——一个是调用时实实在在传进去的,另一个是函数定义时只在纸上写的占位符。今天把它们拆开看。
你手里拿着的原件是实参;函数内部用的形参是复印件,函数一返回就销毁
值传递的内存模型
从栈帧视角看值传递:调用方与被调方各持一份独立变量。
值传递代码演示
用一段完整代码演示整型与浮点型的值传递:函数内修改形参,外部实参纹丝不动。
值传递发生在调用瞬间:实参的值被复制一份给形参。函数内对形参的修改(a = a+10, b = b*2)只作用在这份副本上,因此外部 x、y 完全不受影响。
形参改变会影响实参吗
验证:值传递的本质是单向复制
为什么数组传参特殊
上一页我们看到一个结论:值传递就是把数据完整复制一份给形参。传 4 字节的 int 时几乎没成本,但换成几 MB 甚至几 GB 的数组呢?
一千页全印一份又慢又贵,不如两人共看同一本
数组名的本质
从左到右看:声明数组→分配内存→数组名a指向首地址→a是常量指针
数组传参的内存布局
指针传递:形参接收地址而非副本
一维数组传参代码
完整示例:打印数组、在函数里翻倍每个元素,看 main 里那个数组到底变没变。
L3 形参 int arr[] 实际等价于 int*;L17 真实数组在 main 栈帧;函数里 L12 一改,L26 看到的实参就跟着变——这正是数组传参区别于值传递的关键。
二维数组传参代码
二维数组传给函数时,列维度必须在编译期敲定,看这段代码怎么写才合法。
列维度是编译器计算 arr[i][j] 偏移地址的依据,行数则靠实参传入,因此形参中列必须写死、行可以省略。
数组传参 vs 指针传参
数组传参的 `[]` 外观容易让人误以为整组元素入栈,函数实际收到的是首元素地址。
- 形参可写成 `int a[]`
- 实参可传 `&arr[0]`,`arr` 会转为地址
- 函数内 `sizeof(a)` 只表示指针大小
- 形参写作 `int *p`
- 实参可传 `&x`,或已有指针 `p`
- 函数内 `sizeof(p)` 也只表示指针大小
地址传递的副作用
想想 scanf("%d", &x):不传 &x 它就不知道把读到的数存哪去。这就是地址传递的典型场景——你不光把数据交给函数,还要允许它把结果"写回来"。
他进屋挪家具,你回家布局已变——形参实参共享同一空间
return语句的作用
上一节我们看到,地址传递能让函数直接修改外部数据。但更多时候,函数只想把计算结果'告诉'调用者——这时它需要一个出口,把答案递回去。这个出口就是 return。
投币=传参,按选择键=调用,掉出商品=return返回值
返回值的传递机制
从左到右看返回值的三条传递路径:基本类型走寄存器,大结构体走主调提前准备的栈内存。
返回值使用示例
演示函数返回值的三种典型用法:赋值、嵌入表达式、直接传给其他函数。
L12 把返回值存进变量;L16 返回值直接参与算术运算;L20 返回值不落地,直接喂给 printf。返回值本质就是数据,能出现在任何需要值的位置。
返回值的边界情况
返回值机制已经讲明白了,但有两个边界情况最容易翻车——返回局部变量指针的陷阱,和声明「无返回」的 void。
退房后房卡上的房间号还在,但房内已被新住客重整,下次访问读到的是别人的东西
值传递 vs 地址传递 vs 返回值
传参有进有出:值传递、地址传递负责把数据送进函数,返回值负责把结果带出来。混了方向和机制,调用关系就乱。
- 数据流向:调用方 → 函数内部
- 值传递改不动实参,地址传递可间接修改
- 适合喂数据,或需函数改多个外部变量
- 数据流向:函数内部 → 调用方
- return 返回的是结果副本,调用方接收
- 适合只产出一个结果,或返回状态码
自测:参数传递小测
对函数 void f(int *p) { p = NULL; },调用 f(ptr) 后实参 ptr 的值会怎样?
核心要点回顾
- ✓三种传递通道:值、地址、返回值
- ✓能否修改外部,取决于是否传入地址
- ✓数组名即首地址,数组传参天然按地址走
- ✓选哪种传递方式 = 划定函数的影响边界
课后思考
先合上屏幕想一会儿,再翻参考答案。三个问题各对应一个层次。
参考答案值传递会在栈上为形参分配独立空间,形参只是实参的副本。修改副本不会改动实参所在内存。这正是值传递的隔离特性。
参考答案必须用地址传递。值传递传的是副本,函数内交换的是副本本身,原变量不受影响。地址传递传指针,可直接改原内存。
参考答案返回的是栈上局部变量的地址,函数返回后栈帧销毁,该地址成为悬挂指针,访问是未定义行为。这是返回值的关键边界。