C语言基本笔记(10)—— 函数
函数定义与声明
- 定义:实现函数功能的具体代码块
- 声明:告知编译器函数签名(返回类型、参数列表)
// 声明(头文件中)
int add(int a, int b);
// 定义(源文件中)
int add(int a, int b) {
return a + b;
}
易错点:忘记声明导致编译错误(隐式声明警告)
参数传递方式
| 类型 | 行为 | 内存影响 | 典型场景 |
|---|---|---|---|
| 值传递 | 传递参数副本 | 不影响原数据 | 简单数据类型(int, char) |
| 指针传递 | 传递内存地址 | 直接修改原数据 | 数组、结构体修改 |
| 数组传递 | 退化为指针(等价于指针传递) | 可修改数组元素 | 数组处理函数 |
可变参数函数
#include <stdarg.h>
int sum(int count, ...) {
va_list args;
va_start(args, count);
int total = 0;
for(int i=0; i<count; i++) {
total += va_arg(args, int);
}
va_end(args);
return total;
}
坚决杜绝可变参函数在开发中应用
易错点:类型安全缺失(错误类型访问导致未定义行为)
在嵌入式开发场景下需要主要的点
-
栈空间管理
- 局部变量过大导致栈溢出(常见于递归或大型数组)
- 解决方案:使用静态变量或堆内存(
malloc需谨慎)
-
中断服务函数(ISR)
- 不可重入性:避免在ISR和非ISR代码中共享非原子变量
- 硬件约束:某些平台限制ISR的参数和返回值
-
函数可重入性
- 问题:函数若使用静态变量,在多线程/中断环境下不安全
- 解决方法:改用线程局部存储或避免静态变量
-
值传递 vs 指针传递
- 问题:传递给函数的是原数据的副本还是原数据的地址、修改的是副本还是原数据
- 解决方法:根据是否需要修改原数据来决定传入参数的类型是指针还是值。
-
返回栈内存指针
- 问题:返回局部变量的地址,导致返回值错误
- 解决方法:使用malloc()等函数分配内存。
-
函数指针类型不匹配
- 问题:
float add_float(float a, float b) { return a+b; } int main() { // 错误!类型不匹配但可能编译通过 int (*wrong_ptr)(int, int) = (int (*)(int,int))add_float; printf("%d", wrong_ptr(1, 2)); // 输出不可预测 }- 解决方法:使用严格类型转换、坚决避免隐式类型转换。
浙公网安备 33010602011771号