Logo
Section 1 / 6

按值传递

学习值传递的内部机制和工作原理。

导入

按值传递

学习值传递的内部机制和工作原理。

引言

利用我们对栈和函数调用机制的理解,现在来介绍“传值调用”的概念。

我们的目标是实现一个 swap 函数,以便在整个程序中用于交换两个变量的值。

1#include <stdio.h> 2 3void swap(int a, int b) 4{ 5 int aux = a; 6 a = b; 7 b = aux; 8} 9 10int main() 11{ 12 int a = 5, b = 6; 13 14 printf("交换前: a = %d, b = %d\n", a, b); 15 swap(a, b); 16 printf("交换后: a = %d, b = %d\n", a, b); 17 18 return 0; 19}

让我们运行这段代码并查看输出:

1交换前: a = 5, b = 6 2交换后: a = 5, b = 6

很遗憾!并没有生效。a 和 b 的值都没有改变,但我们确实看到在 swap 函数内部完成了交换操作。

分析代码

我们需要运用新掌握的内存图绘制技巧来理解原因。必须画出 main 函数的栈帧,并展示调用 swap 前所需的准备工作。为了简化图形并聚焦关键点,我们忽略 printf 调用。

swap 函数遵循调用约定:

  • 在其栈帧上方,可以找到返回地址。
  • 在返回地址之上,可以找到它的参数。

当 swap 尝试修改 a 和 b 时,它实际上修改的是紫色部分的变量——这些是为函数调用压入栈中的副本。 swap 函数看不到(也无法修改)main 中的 a 和 b 变量(图中蓝色变量)。

这种方式传递参数被称为 传值调用。 我们传递的是 main 中变量的 值,而不是变量本身。 被调用的函数接收到的是与原始变量具有相同值的副本,它们存储在不同的内存位置,是两个独立的变量。

这里有一个简单的类比:

1int arg = 7; 2int x = arg; 3 4// x 是 arg 的一份拷贝 5// x 是一个不同的变量,拥有自己的内存地址,但值与 arg 相同

传值调用的结论

从传值调用的概念中可以得出以下几点结论:

  • 我们传递的是变量的副本。
  • 当我们希望被调用函数读取变量内容时非常有用。
  • 如果希望在函数内修改变量的值,则毫无用处,因为实际修改的是副本,而非原变量。

为了更清楚地说明最后一点,来看一个简单例子:

1#include <stdio.h> 2 3void change(int x) 4{ 5 x = 11; 6 // x 在 change 内部变为 11 7} 8 9int main() 10{ 11 int x = 5; 12 13 printf("x 之前: %d\n", x); 14 change(x); 15 // 这个 x 没有变化,它是 change 函数内部的那个 x 的不同版本 16 printf("x 之后: %d\n", x); 17 18 return 0; 19}

change 函数中对 x 的修改在 main 中不可见,因为 change 修改的是 x 的一份副本。在 change 函数内部,x 的值是 11,但在 main 中,它的值仍然是 5。

在 第10行 声明的 x 和 change 函数的参数 x 是两个完全不同的变量。 从功能上看,change 函数的函数头写成 void change(int arg) 也完全一样,因为 main 中的 x 与 change 中的参数 x 之间没有任何关联。

复杂指针声明

值传递中的问题