V2.0
王道C++班级参考资料<br />——<br />C语言部分卷3函数<br/>节3值传递<br/><br/>最新版本V2.0
<br>王道C++团队<br/>COPYRIGHT ⓒ 2021-2024. 王道版权所有形参和实参参数传递机制值传递的原理关于指针的补充值传递的优缺点THE END
Gn!
在深入讨论"值传递"之前,我们首先需要理解"函数形参和实参"的区别。主要的差异如下:
定义与调用的区别:
形参:在函数定义时,列在函数名后面的括号中的变量称为形参。形参仅仅是一种占位符,表示在调用该函数时应传递的数据的类型和顺序。
实参:当我们调用一个函数时,我们传递给函数的实际值或变量称为实参。
存储:
形参:它是局部变量的特殊形式,存储在栈区。与一般函数体内的局部变量一样,形参在函数调用时进行初始化。(一般局部变量在函数调用过程中,执行语句时初始化)
实参:实参可以是常量、变量或表达式,具体而言又可以是局部变量、全局变量等,存储位置不定。
生命周期:
形参:它的生命周期仅限于函数调用期间。函数调用结束后,形参作为局部变量就会被销毁。
实参:其生命周期取决于其类型。如果实参是局部变量,它的生命周期与所在的作用域"{}"有关;若为全局变量,则从程序启动到结束等等。
综上,形参和实参在多个方面都存在显著的区别,理解它们是掌握函数传递机制的关键。
Gn!
搞清楚形参和实参的区别后,我们还需要理解"参数传递机制"这个核心概念。
所有存在函数(或方法)的编程语言都有"参数传递机制"的概念,它描述了如何将实参的值传递给函数的形参。
不同的编程语言可能采用不同的参数传递方式。在C语言中,参数传递机制是"值传递(call by value)"的。
值传递意味着,当我们调用一个函数并将实参进行传递时,实际上是:
将实参的值复制给函数的形参。
或者说,将实参的一个副本传递给了函数。
因此,函数内对形参的任何更改都不会影响到实参,因为函数内部修改的只是实参的副本,而不是实参本身。
下面让我们具体讲解这个机制。
Gn!
当一个函数被调用时,实参的值会被复制一份传递给形参,形参得到的只是实参的一个拷贝(副本)。也就是说:
通过函数调用将实参传递给另一个函数时,另一个函数会将此实参取值复制一份,然后在自身栈帧中创建一个新的、独立的局部变量。
而在此函数当中任何"看起来对实参的修改",实际上都是对拷贝副本的修改,对原本实参的值不会产生任何影响。
这就是C语言的值传递机制。
一个非常简单的值传递机制的演示代码如下:
值传递机制-演示代码1
231void swap(int a, int b) {
2int tmp = a;
3a = b;
4b = tmp;
5printf("调用swap函数中,a = %d\n", a);
6printf("调用swap函数中,b = %d\n", b);
7printf("\n");
8}
9
10int main(void) {
11int a = 10;
12int b = 20;
13printf("调用swap函数前,a = %d\n", a);
14printf("调用swap函数前,b = %d\n", b);
15printf("\n");
16
17swap(a, b);
18
19printf("调用swap函数后,a = %d\n", a);
20printf("调用swap函数后,b = %d\n", b);
21
22return 0;
23}
这段代码很好的展示了C语言的值传递机制,我们还可以用一张图来描述这这个机制:
注意:
不要把C语言的值传递机制,理解成局部变量的机制,值传递意味着任何实参通过函数传递,函数接收到的都只是实参的拷贝,哪怕实参本身是一个全局变量这种看起来完全可以跨函数修改的变量,也不影响值传递函数内部得到的只是副本拷贝的本质。
值传递的本质是实参传递的机制,和实参变量本身是什么类型变量以及什么数据类型都没有关系!!!
下面我们看一下很好的例子:
值传递机制-演示代码2
251// 定义全局变量
2int g_var = 10;
3int g_var2 = 20;
4
5void swap_global(int g_var, int g_var2) {
6int tmp = g_var;
7g_var = g_var2;
8g_var2 = tmp;
9printf("调用swap函数中,g_var = %d\n", g_var);
10printf("调用swap函数中,g_var2 = %d\n", g_var2);
11printf("\n");
12}
13
14int main(void) {
15printf("调用swap函数前,g_var = %d\n", g_var);
16printf("调用swap函数前,g_var2 = %d\n", g_var2);
17printf("\n");
18
19swap_global(g_var, g_var2);
20
21printf("调用swap函数后,g_var = %d\n", g_var);
22printf("调用swap函数后,g_var2 = %d\n", g_var2);
23
24return 0;
25}
swap函数用于交换两个全局变量,但需要用函数调用将全局变量作为实参传递,修改能成功吗?
当然是失败的,因为swap函数只得到了两个全局变量的拷贝,在函数体内部修改成功了,但修改成功的是拷贝,对原本实参全局变量没有任何影响。
我们也可以画一个图描述这个过程:
当然全局变量完全可以跨函数修改,只要不使用实参传递的方式即可。比如:
函数修改全局变量-演示代码
51// 此函数调用就会修改全局变量的取值
2void modify_g_var(){
3g_var = 100;
4g_var2 = 200;
5}
这种修改方式就完全没问题。
总之:
在C语言中,任何通过函数直接修改基本数据类型(非指针类型)实参的方式都是不可能成功的,因为函数得到的是实参的拷贝。
Gn!
在上述总结的时候,我们提到了指针,关于指针和值传递,我们需要注意以下几点:
在C语言中,如果想通过函数来修改实参变量的值,是完全可以实现的。但此时函数调用,要传递实参变量的指针,而不是实参变量本身。
无论在任何情况下,C语言都只有值传递,没有诸如"指针传递"、"引用传递"等传值方式。你只需要记住:在任何情况下,通过函数调用将实参传递,函数当中得到都只是实参的拷贝。这不会因实参变量的数据类型,种类等发生改变!!
在C语言中,允许数组作为函数的实参,但数组作为实参传递时比较特殊。这个特殊的行为我们将在学习指针的章节中详细探讨。
Gn!
C语言仅有值传递的这种设计具有以下优点:
安全。由于传递的是实参的副本,所以原始数据不会被修改。这意味着函数对参数的操作不会影响到外部的变量,避免了非预期的副作用,保护了原始数据。
简单直观易懂统一。相比较于C++多种传值方式并存的设计理念,C语言传值方式单一,这体现了C语言简洁统一的设计理念。
缺点也有:
不够灵活,功能弱小。
一些大型数据作为实参时,如果仍然传递拷贝,既占用大量空间,效率也很差。
当然这两个缺点完全可以用指针来弥补,后续给大家讲指针时,还会重提值传递,我们到时候再谈。