C 语言编程教程

C 语言 - 首页

C 语言基础

C 语言 - 概述 C 语言 - 特性 C 语言 - 发展历史 C 语言 - 环境设置 C 语言 - 程序结构 C 语言 - Hello World C - 编译过程 C - 注释 C - 标记 C - 关键字 C - 标识符 C - 用户输入 C - 基本语法 C - 数据类型 C - 变量 C - 整数提升 C - 类型转换 C - 类型转换 C - 布尔值

C 语言中的常量和文字

C - 常量 C - 字面量 C - 转义序列 C - 格式说明符

C 语言中的运算符

C - 运算符 C - 算术运算符 C - 关系运算符 C - 逻辑运算符 C - 位运算符 C - 赋值运算符 C - 一元运算符 C - 递增和递减运算符 C - 三元运算符 C - sizeof 运算符 C - 运算符优先级 C - 其他运算符

C 语言中的决策

C - 决策 C - if 语句 C - if...else 语句 C - 嵌套 if 语句 C - switch 语句 C - 嵌套 switch 语句

C 语言中的循环

C - 循环 C - While 循环 C - For 循环 C - Do...while 循环 C - 嵌套循环 C - 无限循环 C - Break 语句 C - Continue 语句 C - goto 语句

C 语言中的函数

C - 函数 C - Main 函数 C - 按值调用函数 C - 按引用调用函数 C - 嵌套函数 C - 可变参数函数 C - 用户定义函数 C - 回调函数 C - return 语句 C - 递归

C 语言中的作用域规则

C - 作用域规则 C - 静态变量 C - 全局变量

C 语言中的数组

C - 数组 C - 数组的属性 C - 多维数组 C - 将数组传递给函数 C - 从函数返回数组 C - 可变长度数组

C 语言中的指针

C - 指针 C - 指针和数组 C - 指针的应用 C - 指针运算 C - 指针数组 C - 指向指针的指针 C - 将指针传递给函数 C - 从函数返回指针 C - 函数指针 C - 指向数组的指针 C - 指向结构体的指针 C - 指针链 C - 指针 vs 数组 C - 字符指针和函数 C - NULL 指针 C - void 指针 C - 悬垂指针 C - 解引用指针 C - Near、Far 和 Huge 指针 C - 指针数组的初始化 C - 指针与多维数组

C 语言中的字符串

C - 字符串 C - 字符串数组 C - 特殊字符

C 语言的结构体和联合

C - 结构体 C - 结构体和函数 C - 结构体数组 C - 自引用结构 C - 查找表 C - 点 (.) 运算符 C - 枚举(或 enum) C - 结构填充和打包 C - 嵌套结构 C - 匿名结构和联合 C - 联合 C - Bit 位字段 C - Typedef

C 语言中的文件处理

C - 输入和输出 C - 文件 I/O(文件处理)

C 语言中的预处理器

C - 预处理器 C - #pragma 编译指示 C - 预处理器操作符 C - 宏 C - 头文件

C 语言中的内存管理

C - 内存管理 C - 内存地址 C - 存储类

C 其他主题

C - 错误处理 C - 可变参数 C - 命令执行 C - 数学函数 C - static 静态关键字 C - 随机数生成 C - 命令行参数

C 语言编程资源

C语言问题与解答答案 C语言快速指南 C语言速查表 C语言实用资源 C语言讨论


C 语言中的函数按值调用

在 C 语言中,一个函数可以被任何其他函数调用,包括其自身。函数的调用方式有两种:(a) 按值调用和 (b) 按引用调用。默认情况下,采用按值调用机制。

形式参数和实际参数

您必须了解以下术语才能理解按值调用方法的工作原理:

形式参数 − 函数需要某些数据来执行其所需的过程。定义函数时,假定数据值将以参数或参数列表的形式提供在函数名称前面的括号内。这些参数是特定数据类型的变量。

实际参数 − 调用某个函数时,应提供所需数量的值,这些值的类型和顺序应与其定义时相同。

请看以下代码片段 −

type function_name(type var1, type var2, ...)

此处,参数变量被称为形式参数。在函数作用域内,这些变量充当函数的局部变量

考虑以下函数 −

int add(int x, int y){

   int z = x + y;

   return z;

}

此函数定义中的参数 xy 是形式参数。

示例:C 语言中的按值调用

如果调用 add() 函数,如以下代码所示,则括号内的变量(ab)是实际参数。它们会被传递给函数。

请看以下示例 -

#include <stdio.h>

int add(int x, int y){

   int z = x + y;

   return z;
}

int main(){

   int a = 10, b = 20;
   
   int c = add(a, b);

   printf("Addition: %d", c);
}

输出

运行此代码时,将产生以下输出 -

Addition: 30

按值调用方法意味着实际参数的值会被复制到形式参数变量中。因此,"x"取"a"的值,而"b"会被赋值给"y"。add() 函数中的局部变量"z"存储了加法运算的值。在 main() 函数中,add() 函数的返回值会被赋值给"c",并打印出来。

请注意,C 语言中的变量是内存中一个命名的位置。因此,变量是在内存中创建的,并且编译器会为每个变量分配一个随机的内存地址。

C 语言中的"按值调用"是如何工作的?

假设 main() 函数中的变量 abc 分别占据内存位置 100、200 和 300。当使用 ab 作为实际参数调用 add() 函数时,它们的值分别存储在 xy 中。

随机内存地址

变量 xyz 是 add() 函数的局部变量。在内存中,它们将被分配到某个随机位置。假设它们分别在内存地址 1000、2000 和 3000 处创建。

由于该函数的调用方式是将实际参数的值复制到其对应的形式参数变量中,因此 xy 的地址 1000 和 2000 分别将保存 10 和 20。编译器将其加法运算赋值给返回的第三个局部变量 z

当控制权回到 main() 函数时,返回的数据被赋值给 c,并显示为程序的输出。

示例

按值调用是 C 语言中默认的函数调用机制。它消除了函数的潜在副作用,使软件更易于维护和理解。它最适合于需要对接收到的参数进行特定计算并返回结果的函数。

以下是另一个按值调用的示例 -

#include <stdio.h>

/* 函数声明 */
void swap(int x, int y);

int main(){

   /* 局部变量定义 */
   int a = 100;
   int b = 200;

   printf("Before swap, value of a: %d
", a);
   printf("Before swap, value of b: %d
", b);

   /* 调用函数来交换值 */
   swap(a, b);

   printf("After swap, value of a: %d
", a);
   printf("After swap, value of b: %d
", b);

   return 0;
}

void swap(int x, int y){

    int temp;
    
    temp = x; /* 保存 x 的值 */
    x = y; /* 将 y 赋值给 x */
    y = temp; /* 将 temp 赋值给 y */
    
    return;
}

输出

运行此代码时,将产生以下输出 -

Before swap, value of a: 100
Before swap, value of b: 200
After swap, value of a: 100
After swap, value of b: 200

结果显示,尽管值在函数内部发生了变化,但实际上并没有发生变化。

由于值被复制到另一个函数的不同局部变量中,因此任何操作都不会对调用函数中的实际参数变量产生任何影响。

但是,当我们需要将大型对象(例如数组或文件)传递给另一个函数时,按值调用方法效率较低。此外,在某些情况下,我们可能需要由另一个函数操作实际参数。在这种情况下,按值调用机制就显得力不从心了。为此,我们必须探索按引用调用机制。有关 C 语言按引用调用机制的详细说明,请参阅下一章。

按引用调用方法涉及传递保存实际参数值的变量的地址。您可以设计一种混合使用按值调用和按引用调用的调用方法。在这种情况下,一些参数按值传递,另一些按引用传递。