【从0开始跟Chat老师学嵌入式 06】C语言入门 - 函数

小白:C语言中的函数有哪些知识点

Chat老师:

C语言中的函数是程序的基本模块,用于实现代码的重用和结构化。以下是C语言中关于函数的主要知识点:

1. 函数的定义和声明

函数定义
  • 包含函数的返回类型、函数名、参数列表和函数体。

  • 语法:

    返回类型 函数名(参数类型 参数名, ...) {
        // 函数体
    }
    

    示例:

    
    复制代码
    int add(int a, int b) {
        return a + b;
    }
    

函数声明
  • 在函数使用前声明函数,可以放在文件的顶部或头文件中。

  • 语法:

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

    示例:

    int add(int a, int b);
    

2. 函数调用

  • 调用已定义的函数并传递必要的参数。

  • 语法:

    函数名(参数1, 参数2, ...);
    

    示例:

    int result = add(3, 4);
    

3. 返回值

  • 函数可以返回一个值,返回类型为 void 的函数不返回值。
  • 使用 return 语句返回值:
     
    int multiply(int a, int b) {
        return a * b;
    }
    

4. 参数传递

  • 函数可以通过值传递参数,也可以通过指针传递参数来实现引用传递。
值传递
  • 参数的值被复制到函数的局部变量中。
     
    void increment(int a) {
        a++;
    }
    

引用传递
  • 通过指针传递参数,使函数可以修改传入的变量。
     
    void increment(int *a) {
        (*a)++;
    }
    

5. 作用域和生命周期

局部变量
  • 在函数内部声明的变量,仅在该函数内有效,函数调用结束后其生命周期结束。
全局变量
  • 在所有函数外部声明的变量,对所有函数可见,直到程序结束。

6. 递归

  • 函数调用自身,必须有终止条件以防止无限递归。
    int factorial(int n) {
        if (n <= 1) return 1;
        else return n * factorial(n - 1);
    }
    

7. 函数指针

  • 用于指向函数,可以通过函数指针调用函数。
  • int (*funcPtr)(int, int) = add;
    int result = funcPtr(3, 4);
    

8. 可变参数函数

  • 可以接受可变数量的参数,通过 <stdarg.h> 头文件中的 va_listva_startva_argva_end 宏来处理。
    #include <stdarg.h>
    
    void printNumbers(int count, ...) {
        va_list args;
        va_start(args, count);
        for (int i = 0; i < count; i++) {
            int num = va_arg(args, int);
            printf("%d ", num);
        }
        va_end(args);
        printf("\n");
    }
    

9. 内联函数

  • 使用 inline 关键字定义的函数,建议编译器将其内联展开,以减少函数调用的开销。
     
    inline int square(int x) {
        return x * x;
    }
    

10. 标准库函数

  • C标准库提供了大量常用的函数,如 printfscanfstrcpymallocfree 等。
  • 这些函数通常需要包含相关头文件,如 <stdio.h><stdlib.h><string.h> 等。

通过这些知识点的理解和掌握,可以更好地编写和使用C语言中的函数,从而提高代码的模块化、可读性和可维护性。

=========================================================================

小白:使用 inline 关键字定义的函数,建议编译器将其内联展开,以减少函数调用的开销。 这句话什么意思

Chat老师:

使用 inline 关键字定义的函数,建议编译器将其内联展开,以减少函数调用的开销。这句话的意思是:

函数调用的开销

在传统的函数调用中,程序执行流程如下:

  1. 保存当前执行点的上下文(例如寄存器和堆栈信息)。
  2. 跳转到被调用函数的地址。
  3. 执行函数体内的代码。
  4. 返回并恢复先前保存的上下文。
  5. 继续执行调用函数后面的代码。

这个过程需要时间和资源,尤其是在频繁调用简单函数时,函数调用的开销可能会显著影响性能。

内联函数的概念

inline 关键字建议编译器将函数调用直接替换为函数体本身,从而避免上述的函数调用开销。即,编译器在调用函数的地方直接插入函数的代码,而不进行实际的函数调用。

使用 inline 的示例

 
#include <stdio.h>

// 定义一个内联函数
inline int square(int x) {
    return x * x;
}

int main() {
    int num = 5;
    int result = square(num); // 这里会被替换成 result = num * num;
    printf("The square of %d is %d\n", num, result);
    return 0;
}

在上述代码中,调用 square(num) 时,编译器会将其替换为 num * num,而不是生成一个真正的函数调用。这减少了函数调用的开销。

编译器的决定

尽管使用了 inline 关键字,编译器并不一定会内联展开函数。这只是一个建议,编译器可以根据具体情况(例如函数的复杂度、函数体的大小、编译器的优化设置等)决定是否进行内联。

内联的优缺点

优点
  1. 减少函数调用开销:避免了调用和返回的开销,提高了执行效率。
  2. 可能提高性能:尤其是在频繁调用的小函数时,内联展开可以显著提升性能。
缺点
  1. 增加代码体积:每次调用都插入函数体,可能导致最终的可执行文件体积增大(尤其是内联大函数时)。
  2. 编译时间可能增加:由于代码增大,编译时间也可能相应增加。
  3. 缓存命中率降低:增大的代码可能影响CPU缓存的命中率,反而导致性能下降。

综上所述,使用 inline 关键字可以在适当的情况下提高性能,但需要注意内联的实际效果和可能的副作用。

=========================================================================

小白:函数指针再详细举例一下

Chat老师:

函数指针是指向函数的指针,可以用来调用函数或作为参数传递给其他函数。通过函数指针,可以实现更灵活和动态的函数调用。下面是一个详细的示例,演示如何定义、使用和传递函数指针。

定义和使用函数指针

定义函数指针

首先,我们定义一个普通的函数,然后定义一个指向该函数的指针。

#include <stdio.h>

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

int main() {
    // 定义一个函数指针,指向返回int,接收两个int参数的函数
    int (*funcPtr)(int, int);

    // 将函数指针指向函数add
    funcPtr = &add;

    // 通过函数指针调用函数
    int result = funcPtr(3, 4);
    printf("The result of add(3, 4) is %d\n", result);

    return 0;
}

作为参数传递函数指针

函数指针可以作为参数传递给另一个函数,这在实现回调函数时特别有用。

#include <stdio.h>

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

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

// 定义一个接受函数指针作为参数的函数
void process(int (*func)(int, int), int x, int y) {
    int result = func(x, y);
    printf("The result is %d\n", result);
}

int main() {
    // 调用process函数,并传递add和multiply函数的指针
    process(add, 3, 4);      // 输出:The result is 7
    process(multiply, 3, 4); // 输出:The result is 12

    return 0;
}

函数指针数组

函数指针数组可以用于动态调用多个不同的函数。

#include <stdio.h>

// 定义几个普通函数
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;
}

// 定义一个函数指针数组
int (*funcArr[3])(int, int) = { add, subtract, multiply };

int main() {
    int x = 5, y = 3;

    // 通过函数指针数组调用函数
    for (int i = 0; i < 3; i++) {
        printf("Result: %d\n", funcArr[i](x, y));
    }

    return 0;
}

使用 typedef 简化函数指针定义

使用 typedef 可以简化函数指针的定义和使用。

#include <stdio.h>

// 定义一个函数指针类型
typedef int (*operation)(int, int);

// 定义几个普通函数
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;
}

// 使用 typedef 定义函数指针数组
operation operations[3] = { add, subtract, multiply };

int main() {
    int x = 6, y = 2;

    for (int i = 0; i < 3; i++) {
        printf("Result: %d\n", operations[i](x, y));
    }

    return 0;
}

通过上述示例,展示了函数指针的基本用法、作为参数传递、函数指针数组以及如何使用 typedef 简化函数指针的定义和使用。这些技术在编写灵活和可扩展的C代码时非常有用。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值