26 C 语言函数深度解析:定义与调用、返回值要点、参数机制(值传递)、原型声明、文档注释

发布于:2025-05-31 ⋅ 阅读:(18) ⋅ 点赞:(0)

1 函数基础概念

1.1 引入函数的必要性

        在《街霸》这类游戏中,实现出拳、出脚、跳跃等动作,每项通常需编写 50 - 80 行代码。若每次调用都重复编写这些代码,程序会变得臃肿不堪,代码可读性与维护性也会大打折扣。

        为解决这一问题,可将出拳、出脚、跳跃相关的代码提取出来,用大括号 {} 包裹,并为这段代码命名。之后在需要执行这些动作的地方,通过名称调用这段代码即可。提取出的这部分代码,就是程序中的函数。游戏执行相应动作时,调用对应函数即可,此方法减少了代码重复,提升了程序整洁度与可维护性。

1.2 什么是函数

        函数是可重复使用的代码块,用于执行特定任务或操作。它能够将代码逻辑组织成独立单元,进而提高代码的可读性、可维护性与重用性。

        在 C 语言中,一个程序可由一个或多个源文件(扩展名为 .c)组成。每个源文件都是编译单位,可包含多个函数,这些函数间能相互调用,所以函数是 C 程序的基本构成单元

1.3 函数的作用

  • 封装功能:将完整功能封装成函数,提升代码结构化程度与复用性。
  • 代码模块化:按功能将程序拆分为多个模块单元,降低程序复杂度。
  • 增强可维护性:若需修改某项功能,只需调整对应函数的代码。
  • 隔离细节:通过函数调用隐藏实现细节,调用方只需关注输入输出。

1.4 函数的分类

        从使用角度看,C 语言中的函数可分为两类:

  • 库函数(标准函数):由 C 系统提供,用户无需自行定义,可直接使用。使用库函数时,必须包含对应的头文件(#include 语句)
  • 自定义函数:为满足具体需求而自行定义的函数,需先定义后使用

2 函数的定义与调用

2.1 函数的定义

        函数定义提供了函数的具体实现代码(函数体),包含函数完成任务的所有指令。其结构如下:

返回类型 函数名(参数列表)
{
    函数体语句1;
    函数体语句2;
    …………………………
    return 返回值; // 若返回类型为 void,可省略 return
}

结构说明:

  • 函数名:调用函数时使用的名称,需符合标识符规范
  • 函数体:实现函数功能的代码块。
  • 参数列表(形参列表):接收调用时传递的值(实参)
  • 返回值:函数执行后返回的值,类型需与返回类型一致。无返回值时,返回类型为 void

2.2 案例:函数定义

        以下代码演示了 C 语言中函数的定义:

#include <stdio.h>

// 无参数无返回值的函数
void func()
{
    printf("hello func\n");
}

// 计算两整数差值的函数
int minus(int m, int n)
{
    int result = m - n; // 先计算结果
    return result;      // 再返回结果
}

// 计算两浮点数和的函数
double add(double i, double j)
{
    return i + j; // 也可以直接返回结果
}

// 返回两整数中较大值的函数
int max(int a, int b)
{
    return a > b ? a : b;
}

// 主函数
int main()
{
    return 0;
}

2.3 注意:函数不能嵌套定义

        C 语言中,函数是独立的,不可嵌套定义。一个函数不能定义在另一个函数内部。

int func1(int a, int b)
{   
    // 错误:C 语言不允许在函数内部定义另一个函数
    int func2(int c, int d)  
    {   
        // ...
    }
}

        某些编译器扩展支持嵌套定义,但不符合 C 标准,影响可移植性,强烈不建议使用。

2.4 函数的调用

        语法:通过函数名 + 圆括号 () 调用函数,参数(实参)写在括号内,多个参数用逗号分隔

        执行机制:每次调用函数时,函数体内的代码会完整执行一次,从入口到返回或结束

#include <stdio.h>

// 无参数无返回值的函数
void func()
{
    printf("hello func\n");
}

// 计算两整数差值的函数
int minus(int m, int n)
{
    int result = m - n; // 先计算结果
    return result;      // 再返回结果
}

// 计算两浮点数和的函数
double add(double i, double j)
{
    return i + j; // 也可以直接返回结果
}

// 返回两整数中较大值的函数
int max(int a, int b)
{
    return a > b ? a : b;
}
// 主函数
int main()
{
    // 调用无参函数
    func(); // 每次调用函数,函数体内的语句会执行一次
    func(); // 每次调用函数,函数体内的语句会执行一次

    // 调用有参函数并打印结果
    printf("10-20的结果:%d\n", minus(10, 20)); // -10
    printf("20-10的结果:%d\n", minus(20, 10)); // 10

    // 传递变量和字面量作为参数
    double d1 = 10.0, d2 = 90.0;
    printf("10.0+90.0的结果:%.2f\n", add(d1, d2));     // 100.00
    printf("20.0+80.0的结果:%.2f\n", add(20.0, 80.0)); // 100.00

    // 比较并打印较大值
    printf("66和88之间较大的是:%d\n", max(66, 88)); // 88
    printf("45和31之间较大的是:%d\n", max(45, 31)); // 45

    // 对返回值进行操作
    printf("max(66,88) + max(12,6) = %d\n", max(66, 88) + max(12, 6)); // 88+12=100

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:


3 函数的返回值

        函数调用后可返回一个确定的值,称为返回值。返回值通常表示计算结果或函数执行状态。

3.1 无返回值类型

        函数无返回值时,使用 void 作为返回类型

正常情况

        void 函数仅执行操作,不返回任何值。

#include <stdio.h>

void fun01() // 无返回值函数
{
    printf("调用了 fun01 函数\n");
}

int main()
{
    fun01(); // 调用无返回值函数
    
    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

硬要返回一个值

        若尝试从 void 函数返回一个值(如通过 return 返回具体值),会导致编译错误或警告

#include <stdio.h>

void fun02() // 声明为 void 函数,但尝试返回值
{
    printf("调用了 fun02 函数\n");

    return 666; // 编译错误:void 函数不能返回值
}

int main()
{
    fun02(); // 调用函数(编译报错)

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

3.2 有返回值类型

正常情况

        明确指定返回值类型(如 int、double),并通过 return 返回具体值。

#include <stdio.h>

double fun02() // 返回 double 类型
{
    return 3.1415926;
}

int main()
{
    printf("fun02() 返回的数据:%.2f\n", fun02()); // 输出 3.14

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

无 return 语句

        若函数声明了非 void 返回类型但未包含 return 语句,将返回不确定值(未定义行为)

#include <stdio.h>

int fun03() // 声明返回 int 但无 return 语句
{
    10 + 20; // 仅计算,未返回
    // return;  只有 return,没有返回值,效果和没有 return 一样
}

int main()
{
    printf("fun03() 返回的数据(不确定值):%d\n", fun03()); // 输出不确定值

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

返回类型不一致

        若 return 表达式类型与函数声明类型不一致,编译器会尝试隐式转换(如 int → float),但需注意转换的安全性

  • 安全转换低精度 → 高精度(如 int → double),数据无损失。
  • 不安全转换:高精度 → 低精度(如 double → int),可能导致数据截断。
#include <stdio.h>

int fun04() // 声明返回 int,但 return 值为 double
{
    return 20.89; // 隐式转换为 int,丢失小数部分
}

int main()
{
    printf("fun04() 返回的数据(精度损失):%d\n", fun04()); // 输出 20
    
    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

3.3 return 语句的作用

返回值传递

        若函数声明了返回类型(非 void),return 语句将函数内部的计算结果返回给调用者

int add(int a, int b) {  
    return a + b; // 返回两数之和  
}

终止函数执行

        return 语句会立即终止当前函数的执行,并跳转回调用位置

  • 无论 return 出现在函数体的何处(如循环、条件分支),后续代码均不再执行。
  • 适用于提前退出函数(如错误处理或条件满足时)。
int divide(int a, int b) {  
    if (b == 0) {  
        return -1; // 除数为零时提前终止,返回错误码  
        // return;    // 或者直接退出函数
    }  
    return a / b; // 仅当 b != 0 时执行  
}

多 return 路径

        函数可通过不同条件分支包含多个 return 语句,实现灵活的控制流

  • 根据输入参数或状态选择不同的返回值或退出路径。
  • 替代 if-else 嵌套,提升代码可读性。
int check_grade(int score) {  
    if (score >= 90) return 'A';  
    if (score >= 80) return 'B';  
    if (score >= 70) return 'C';  
    return 'D'; // 默认路径  
}

4 函数的参数

        函数的参数分为形式参数(形参)实际参数(实参)

4.1 形参与实参

  • 形参:在函数定义或声明时,函数名后括号 () 中定义的变量,函数的 “输入接口”,生命周期短暂,仅在函数内有效
  • 实参:在函数调用时,函数名后括号 () 中使用的常量、变量或表达式,外部数据的 “传递媒介”,可以是任意合法表达式
类别 形参(Formal Parameter) 实参(Actual Parameter)
定义位置 函数定义或声明时,括号 () 内定义的变量 函数调用时,括号 () 内传递的具体值(常量、变量、表达式)
作用 接收外部传入的数据,作为函数内部处理的临时变量 提供实际数据,初始化形参的值
作用域 仅在函数体内有效(局部作用域),函数结束后销毁 作用域由实参本身决定(如变量实参的作用域在其定义的位置)
存储位置 栈内存(函数调用时分配,结束后释放) 取决于实参类型(如变量实参存储在原有作用域的内存中)

4.2 参数传递 → 值传递

        C 语言中,函数调用时实参的值会被复制给形参(即 “值传递”)。形参相当于一个临时变量,存储实参的值,修改形参不影响实参

  • C 语言不支持引用传递,但可通过传递地址间接实现(后续学习)
  • 若实参数量与形参数量不一致,编译器会报错
#include <stdio.h>

// 函数定义,接受两个整数参数,返回它们的和
int func(int x, int y)
{
    return x + y; // 返回两个整数的和
}

int main()
{
    int sum = func(3, 5);                       // 调用 func 函数,实参为 3 和 5
    printf("func(3, 5) 返回的数为:%d\n", sum); // 输出:8

    // 实参与形参数量不一致时,编译报错
    // func(100, 299, 300); // 错误:func 函数只接受两个参数
    // func(100);           // 错误:func 函数需要两个参数

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:

        实参与形参数量必须一致,否则编译报错,如下所示:

        C 语言默认采用值传递:实参的值被复制给形参,函数内对形参的修改不会影响实参

#include <stdio.h>

// 交换两个变量的值
void swap(int a, int b)
{
    // 注意:实参的值被复制给形参,函数内对形参的修改不会影响实参!!!
    int temp = a;
    a = b;
    b = temp;
}

int main()
{
    int x = 1, y = 2;
    printf("交换前:x=%d, y=%d\n", x, y); // 输出:x=1, y=2

    swap(x, y); // 调用后 x 和 y 的值不变

    // 注意:实参的值被复制给形参,函数内对形参的修改不会影响实参!!!
    printf("交换后:x=%d, y=%d\n", x, y); // 输出:x=1, y=2

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:


5 文档注释

        在 C 语言中,文档注释是一种用于生成函数或代码说明的特殊注释格式,便于开发人员理解代码功能。以下是常见的文档注释标签及其用途:

5.1 常用文档注释标签

  • @brief简要描述函数的功能。
  • @param描述函数的每个参数(格式:@param 参数名 参数说明)。
  • @return描述函数的返回值(仅用于有返回值的函数)。
  • @note补充说明函数的注意事项或其他信息。
  • @warning提示函数的使用风险或潜在问题。

5.2 工具支持

  • VS Code:输入 /** 后按回车,可自动生成文档注释模板
  • Doxygen:通过解析文档注释生成代码文档(如 HTML、PDF 格式)。

5.3 案例演示

#include <stdio.h>

/**
 * @brief 计算两个整数的和
 *
 * @param x 第一个整数
 * @param y 第二个整数
 * @return 返回 x 和 y 的和
 * @note 此函数仅支持整数加法,不处理溢出问题
 */
int func(int x, int y)
{
    return x + y; // 返回两个整数的和
}

int main()
{
    int sum = func(3, 5);
    printf("%d\n", sum); // 输出:8

    return 0;
}

        在 VS Code 中,将鼠标悬停在函数名上即可查看对应的文档注释,便于快速理解函数的功能、参数及返回值信息。


6 函数的原型声明

        在 C 语言中,函数原型声明(Function Prototype)是编译器处理函数调用的重要依据。

6.1 函数原型的作用

        提前告知编译器接口信息:函数必须 “先声明后使用”。若函数定义在 main() 或其他调用代码之后,需通过原型声明提前告知编译器函数的返回类型和参数列表,确保调用时能正确检查参数类型和返回值

        避免隐式声明风险:未声明直接调用函数时,编译器会假设返回类型为 int 并尝试猜测参数类型(C89 标准),可能导致未定义行为。函数原型可消除此类隐患。

6.2 函数原型的语法

        函数原型声明需包含以下内容(不包含函数体):

  1. 返回类型:与函数定义一致(如 int、void)。
  2. 函数名:与定义完全匹配(区分大小写)。
  3. 参数列表:
    • 必须指定参数类型(如 int、double)
    • 形参名称可选(仅用于文档说明,编译器忽略)
// 完整形式(推荐,提升可读性)  
int add(int a, int b);  

// 省略形参名(仅保留类型)  
int add(int, int);
  • 分号必需:函数原型以分号结尾,区别于函数定义
  • void 参数:若函数无参数,需显式写 void(如 int func(void);),否则可能被误认为接受任意参数(C 语言旧标准)。

6.3 案例演示

#include <stdio.h>

// 函数原型声明(分号必需,形参名称可省略)
int twice1(int num1, int num2); // 完整形参名,推荐,提升可读性
int twice2(int, int, int);      // 省略形参名

int main(void)
{
    int result1 = twice1(10, 5);             // 调用函数
    printf("twice1(10, 5) = %d\n", result1); // 输出:30

    int result2 = twice2(10, 5, 2);             // 调用函数
    printf("twice2(10, 5, 2) = %d\n", result2); // 输出:34

    return 0;
}

// 函数 twice1 的定义
/**
 * @brief 返回两个整数和乘以 2 的结果
 * @param num1 第一个整数
 * @param num2 第二个整数
 * @return 返回 int 类型
 */
int twice1(int num1, int num2)
{
    return (num1 + num2) * 2;
}

// 函数 twice2 的定义
/**
 * @brief 返回三个整数和乘以 2 的结果
 * @param num1 第一个整数
 * @param num2 第二个整数
 * @param num3 第三个整数
 * @return 返回 int 类型
 */
int twice2(int num1, int num2, int num3)
{
    return (num1 + num2 + num3) * 2;
}

        程序在 VS Code 中的运行结果如下所示: