深入理解C语言之函数

344 阅读6分钟

image.png

前言

继上一篇 深入理解C语言之函数 的文章推出后,应学弟学妹们的请求,继续推出这篇函数的文章。

掌握一门知识从来都不是那么容易的,只有从最基本的概念学起,然后经过不断实践反思总结,最后达到能教给别人的程度,才能算得上初步的掌握了知识点。

千曲而后晓声,观千剑而后识器。虐它千百遍方能通晓其真意

一、基本概念

1.1、定义

函数C语言中实现模块化编程基本单位。一个函数由函数头函数体组成。

  • 函数头
    • 返回类型:指定函数返回值的数据类型。
    • 函数名:标识函数的名字。
    • 参数列表:传递给函数的变量列表,可以为空。
  • 函数体
    • 包含实际执行的代码块,可以有多条语句
    • 可以使用return语句返回值

1.2、语法

返回类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...) {
    // 函数体
    // 可以有多条语句
    // 可以使用 return 语句返回值
}

二、函数的类型

2.1、无参数、无返回值

// 定义一个无参数、无返回值的函数
void printHello() {
    printf("Hello, World!\n");
}

2.2、带参数、无返回值

// 定义一个带参数、无返回值的函数  
void printNumber(int a) {  
    printf("%d\n", a);  
}

2.3、无参数、有返回值

// 定义一个无参数、有返回值的函数  
int getNumber() {  
    return 4; // 返回一个固定的数  
}

2.4、有参数、有返回值

// 定义一个带参数、有返回值的函数  
int add(int a) {  
    int b = 10;
    return a + b;  
}

2.5、有多个参数、无返回值

// 定义一个带有多个参数、有返回值的函数  
void calculateAverage(double a, double b, double c) {  
    double c =  (a + b + c) / 3.0;  
    printf("Average: %.2f\n", average);
}

2.6、有多个参数、有返回值

// 定义一个带有多个参数、有返回值的函数  
double calculateAverage(double a, double b, double c) {  
    return (a + b + c) / 3.0;  
}

三、函数的声明及调用

调用函数之前必须先声明或定义该函数。如果函数定义在 main 函数之后,需要在 main 函数之前进行函数声明。

通过函数名加上括号来调用函数,如果函数有参数,则需要在括号内传入相应的参数。

3.1、语法

返回类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...);

3.2、声明及调用

#include <stdio.h>

// 函数声明的语法结构
// 返回类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...);

// 函数声明
int add(int a, int b);

int main() {
    int result = add(5, 3);
    printf("Result: %d\n", result);
    return 0;
}

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

四、参数传递与作用域

  • C语言支持两种参数传递方式:值传递地址传递
  • 局部变量:在函数内部声明的变量,其作用域仅限于该函数
  • 全局变量:在函数之外声明的变量,可以在整个文件中访问

4.1、值传递

传递的是参数的副本函数内部对参数的修改不会影响到调用者

示例说明

void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

int main(void) {
    int x = 10;
    int y = 20;
    printf("Before swap: x = %d, y = %d\n", x, y);
    swap(x, y);
    printf("After swap: x = %d, y = %d\n", x, y);
    return 0;
}

输出:
Before swap: x = 10, y = 20
After swap: x = 10, y = 20

4.2、地址传递

传递的是参数的地址函数内部可以通过指针修改调用者的实际参数

示例说明

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main(void) {
    int x = 10;
    int y = 20;
    printf("Before swap: x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("After swap: x = %d, y = %d\n", x, y);
    return 0;
}

输出:
Before swap: x = 10, y = 20
After swap: x = 20, y = 10

五、递归及递归函数

5.1、概述

  • 递归
    • 是一种解决计算问题的方法,其中解决方案取决于同一类问题的更小子集
  • 递归函数
    • 是指函数调用自身的函数。递归函数必须有一个明确的终止条件否则会导致无限递归
  • 详细说明
    • 若每个函数对应一种解决方案, 自己调用自己意味着解决方案是一致的(有规律的)。
    • 每次调用,函数处理的数据会较上次缩减(子集),而且最后会缩减至无需递归
    • 内层函数调用(子集处理)完成外层函数才能算调用完成
    • 深入最里层叫做
    • 从最里层出来叫做
    • 在递的过程中,外层函数内的局部变量(以及方法参数)并未消失,归的时候可以用到。

5.2、代码示例

#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;
    printf("Factorial of %d is %d\n", num, factorial(num));
    return 0;
}

//详细执行流程说明
//1.初始执行
int factorial(int 5) {
    if (n == 0 || n == 1) {
        return 1;
    } else {
        //2.第一次递归调用
  return 5*factorial(int 4) {
            if (n == 0 || n == 1) {
                return 1;
            } else {
                //3.第二次递归调用
        return 4*factorial(int 3) {
                    if (n == 0 || n == 1) {
                        return 1;
                    } else {
                        //4.第三次递归调用
                   return 3*factorial(int 2) {
                            if (n == 0 || n == 1) {
                                return 1;
                            } else {
                                //5.第四次递归调用
                         return 2*factorial(int 1) {
                                    if (n == 0 || n == 1) {
                                        return 1;
                                    } else {
                                       return n* factorial(n-1);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

5.3、执行流程

  • 1、初始调用
factorial(5)
  • 2、第一次递归调用
return 5 * factorial(4)
  • 3、第二次递归调用
return 4 * factorial(3)
  • 4、第三次递归调用
return 3 * factorial(2)
  • 5、第四次递归调用
return 2 * factorial(1)
  • 6、基本终止条件
return 1
  • 7、回溯计算

  • factorial(2) 返回 2 * 1 = 2。

  • factorial(3) 返回 3 * 2 = 6。

  • factorial(4) 返回 4 * 6 = 24。

  • factorial(5) 返回 5 * 24 = 120。

  • 8、最终输出

  • Factorial of 5 is 120

5.4、小结

  • 递归函数factorial函数通过递归调用自身来计算阶乘。
  • 终止条件:当 n01 时,递归终止
  • 回溯计算:每次递归调用的结果被用来计算上一层的值,直到最初的调用完成。

六、回调函数

回调函数是一种常见的编程模式,它允许你将一个函数作为参数传递给另一个函数,并在适当的时候调用这个函数。回调函数通常用于事件处理异步操作通用算法中。

实现步骤

  • 声明:确定回调函数的签名返回类型参数列表)。
  • 编写:实现具体的回调逻辑。
  • 传递:将回调函数的地址作为参数传递给另一个函数。
  • 调用:在适当的时候调用传递进来的回调函数

示例代码

#include <stdio.h>

// 1、定义回调函数
typedef void (*Callback)(int);

// 2、实现具体的回调函数
void callbackFunction(int value) {
    printf("value: %d\n", value);
}

// 3、定义一个接受回调函数的函数
void performOperation(Callback cb, int value) {
    // 执行一些操作
    printf("other operation...\n");
    // 4、调用回调函数
    if (cb != NULL) {
        cb(value);
    }
}

int main() {
    // 传递回调函数并调用
    performOperation(callbackFunction, 42);
    return 0;
}

输出:
other operation...
value: 42

七、总结

C语言中的函数是实现模块化编程的关键工具,通过函数可以将代码组织成可重用的模块,提高代码的可读性可维护性开发效率。熟练掌握函数的定义声明参数传递返回值递归等基本概念至关重要,能为我们今后的搬砖工作打下坚实的基础。

码字不易,记得 关注 + 点赞 + 收藏 + 评论