Skip to content

函数

在C语言中,函数是程序的基本执行单元,用于封装特定的功能。通过使用函数,我们可以将复杂的程序分解为更小、更易管理的部分,提高代码的可读性、可维护性和重用性。在本章节中,我们将学习C语言中函数的定义、声明、调用和使用方法。

1. 什么是函数?

函数可以看作是一个命名的代码块,它执行特定的任务,并可能返回一个值。函数就像一个工厂,接收输入(参数),进行处理,然后产生输出(返回值)。

例如,想象一个计算圆面积的函数:

  • 输入:圆的半径
  • 处理:使用公式πr²计算面积
  • 输出:圆的面积

2. 函数的定义

定义函数就是创建一个函数,指定其名称、参数和函数体。函数定义的基本语法如下:

c
返回类型 函数名(参数列表) {
    // 函数体:函数要执行的代码
    return 返回值;
}

2.1 各部分说明

  • 返回类型:函数执行完毕后返回的值的类型,如果函数不返回值,则使用void
  • 函数名:函数的名称,遵循标识符命名规则
  • 参数列表:函数接收的参数,格式为类型 参数名,多个参数用逗号分隔,如果没有参数,则使用void或空括号
  • 函数体:函数要执行的代码,用花括号{}包围
  • return语句:用于返回值并结束函数执行

2.2 示例

c
// 定义一个计算圆面积的函数
float calculate_area(float radius) {
    float area = 3.14159 * radius * radius;
    return area;
}

// 定义一个打印问候语的函数
void greet() {
    printf("Hello, World!\n");
}

3. 函数的声明

声明函数是告诉编译器函数的存在,包括其名称、返回类型和参数列表。函数声明的基本语法如下:

c
返回类型 函数名(参数列表);

3.1 为什么需要函数声明?

当函数定义在调用它的代码之后时,需要先声明函数,以便编译器在编译时知道函数的存在。

3.2 示例

c
#include <stdio.h>

// 函数声明
float calculate_area(float radius);
void greet();

int main() {
    // 调用函数
    greet();
    float area = calculate_area(5.0);
    printf("面积: %.2f\n", area);
    return 0;
}

// 函数定义
float calculate_area(float radius) {
    float area = 3.14159 * radius * radius;
    return area;
}

void greet() {
    printf("Hello, World!\n");
}

4. 函数的调用

调用函数就是执行函数中的代码。函数调用的基本语法如下:

c
函数名(实际参数列表);

4.1 示例

c
#include <stdio.h>

// 函数定义
int add(int a, int b) {
    return a + b;
}

int main() {
    int x = 5, y = 3;
    // 调用add函数,并将返回值赋给sum变量
    int sum = add(x, y);
    printf("%d + %d = %d\n", x, y, sum);
    return 0;
}

5. 参数传递

C语言中,函数参数的传递方式有两种:

5.1 值传递

值传递是将实际参数的值复制一份传递给形式参数,函数内部修改形式参数不会影响实际参数。

5.1.1 示例

c
#include <stdio.h>

void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    printf("函数内部: a = %d, b = %d\n", a, b);
}

int main() {
    int x = 5, y = 10;
    printf("交换前: x = %d, y = %d\n", x, y);
    swap(x, y);
    printf("交换后: x = %d, y = %d\n", x, y);
    return 0;
}

输出结果:

交换前: x = 5, y = 10
函数内部: a = 10, b = 5
交换后: x = 5, y = 10

可以看到,函数内部的交换操作并没有影响到主函数中的变量x和y。

5.2 地址传递

地址传递是将实际参数的地址传递给形式参数,函数内部通过指针修改形式参数指向的值,会影响实际参数。

5.2.1 示例

c
#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    printf("函数内部: *a = %d, *b = %d\n", *a, *b);
}

int main() {
    int x = 5, y = 10;
    printf("交换前: x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("交换后: x = %d, y = %d\n", x, y);
    return 0;
}

输出结果:

交换前: x = 5, y = 10
函数内部: *a = 10, *b = 5
交换后: x = 10, y = 5

可以看到,函数内部的交换操作影响到了主函数中的变量x和y。

6. 返回值

函数可以通过return语句返回一个值,返回值的类型必须与函数定义中的返回类型匹配。

6.1 示例

c
#include <stdio.h>

// 返回两个数中的最大值
int max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

// 不返回值的函数
void print_hello() {
    printf("Hello!\n");
    // 可以省略return语句
}

int main() {
    int x = 5, y = 10;
    int maximum = max(x, y);
    printf("最大值: %d\n", maximum);
    print_hello();
    return 0;
}

7. 函数的分类

根据函数的参数和返回值,函数可以分为以下几类:

7.1 无参无返回值函数

c
void function() {
    // 函数体
}

7.2 无参有返回值函数

c
int function() {
    // 函数体
    return 10;
}

7.3 有参无返回值函数

c
void function(int a, float b) {
    // 函数体
}

7.4 有参有返回值函数

c
float function(int a, float b) {
    // 函数体
    return a * b;
}

8. 函数的嵌套调用

嵌套调用是指在一个函数的函数体中调用另一个函数。

8.1 示例

c
#include <stdio.h>

// 计算两个数的和
int add(int a, int b) {
    return a + b;
}

// 计算三个数的和
int add_three(int a, int b, int c) {
    return add(add(a, b), c);  // 嵌套调用add函数
}

int main() {
    int sum = add_three(1, 2, 3);
    printf("1 + 2 + 3 = %d\n", sum);
    return 0;
}

9. 函数的递归调用

递归调用是指函数调用自身的过程。递归是一种强大的编程技术,用于解决可以分解为相同子问题的问题。

9.1 示例:计算阶乘

c
#include <stdio.h>

// 递归计算阶乘
int factorial(int n) {
    if (n == 0 || n == 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

int main() {
    int num = 5;
    int result = factorial(num);
    printf("%d的阶乘是: %d\n", num, result);
    return 0;
}

输出结果:

5的阶乘是: 120

9.2 递归的条件

使用递归时,必须满足以下条件:

  1. 基准情况:存在一个或多个不需要递归调用就能解决的情况
  2. 递归情况:问题可以分解为更小的子问题,并且子问题的解法与原问题相同

10. 函数的参数

10.1 形式参数

形式参数(简称形参)是函数定义中声明的参数,用于接收实际参数的值。

c
void function(int x, float y) {  // x和y是形式参数
    // 函数体
}

10.2 实际参数

实际参数(简称实参)是函数调用时传递给函数的参数。

c
function(10, 3.14);  // 10和3.14是实际参数

10.3 参数传递规则

  • 实际参数的数量和类型必须与形式参数匹配
  • 实际参数可以是常量、变量或表达式

11. 函数的存储类别

函数的存储类别决定了函数的作用域和链接属性。C语言中,函数的存储类别有以下几种:

11.1 外部函数

外部函数是默认的函数存储类别,可以在其他文件中调用。

c
// 外部函数(默认)
int function() {
    // 函数体
}

11.2 静态函数

静态函数只能在定义它的文件中调用,不能在其他文件中调用。

c
// 静态函数
static int function() {
    // 函数体
}

12. 内联函数

内联函数是一种特殊的函数,编译器会将内联函数的代码直接插入到调用它的地方,而不是通过函数调用的方式执行,从而提高程序的执行效率。

12.1 语法

c
inline 返回类型 函数名(参数列表) {
    // 函数体
}

12.2 示例

c
#include <stdio.h>

// 内联函数
inline int max(int a, int b) {
    return a > b ? a : b;
}

int main() {
    int x = 5, y = 10;
    int maximum = max(x, y);
    printf("最大值: %d\n", maximum);
    return 0;
}

13. 函数的默认参数

C语言不直接支持函数的默认参数,但可以通过函数重载或可变参数来模拟。

14. 函数的可变参数

C语言支持可变参数函数,即函数可以接收数量不确定的参数。

14.1 示例:计算平均值

c
#include <stdio.h>
#include <stdarg.h>

// 计算多个数的平均值
float average(int count, ...) {
    va_list args;
    va_start(args, count);
    
    float sum = 0.0;
    for (int i = 0; i < count; i++) {
        sum += va_arg(args, int);
    }
    
    va_end(args);
    return sum / count;
}

int main() {
    float avg1 = average(3, 1, 2, 3);
    float avg2 = average(5, 1, 2, 3, 4, 5);
    printf("平均值1: %.2f\n", avg1);
    printf("平均值2: %.2f\n", avg2);
    return 0;
}

15. 示例:综合运用

让我们看一个综合运用函数的例子:

c
#include <stdio.h>

// 函数声明
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
float divide(int a, int b);
void print_menu();

int main() {
    int choice, num1, num2;
    
    do {
        print_menu();
        printf("请选择操作: ");
        scanf("%d", &choice);
        
        if (choice == 5) {
            printf("退出程序\n");
            break;
        }
        
        printf("请输入两个数: ");
        scanf("%d %d", &num1, &num2);
        
        switch (choice) {
            case 1:
                printf("%d + %d = %d\n", num1, num2, add(num1, num2));
                break;
            case 2:
                printf("%d - %d = %d\n", num1, num2, subtract(num1, num2));
                break;
            case 3:
                printf("%d * %d = %d\n", num1, num2, multiply(num1, num2));
                break;
            case 4:
                if (num2 == 0) {
                    printf("错误:除数不能为零\n");
                } else {
                    printf("%d / %d = %.2f\n", num1, num2, divide(num1, num2));
                }
                break;
            default:
                printf("无效的选择\n");
                break;
        }
    } while (1);
    
    return 0;
}

// 函数定义
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

float divide(int a, int b) {
    return (float)a / b;
}

void print_menu() {
    printf("\n菜单:\n");
    printf("1. 加法\n");
    printf("2. 减法\n");
    printf("3. 乘法\n");
    printf("4. 除法\n");
    printf("5. 退出\n");
}

16. 注意事项

  1. 函数名:函数名应该具有描述性,能够表达函数的功能
  2. 函数的长度:函数应该保持简短,只做一件事情
  3. 函数的参数:函数的参数不宜过多,一般不超过5个
  4. 函数的返回值:函数的返回值应该与函数的功能相符
  5. 递归的使用:递归虽然强大,但可能会导致栈溢出,应谨慎使用
  6. 内联函数的使用:内联函数只适用于简短的函数,否则会增加代码大小

17. 小结

函数是C语言中重要的组成部分,它具有以下优点:

  1. 代码重用:函数可以在多个地方调用,避免代码重复
  2. 模块化:将复杂的程序分解为更小、更易管理的部分
  3. 可读性:函数名和注释可以使代码更易理解
  4. 可维护性:修改函数的实现不会影响调用它的代码
  5. 可测试性:函数可以单独测试,确保其正确性

在实际编程中,应合理使用函数,将复杂的任务分解为简单的函数,提高代码的质量和开发效率。