814 字
2 分钟
【C】函数
函数基础
声明与定义
// 声明(原型)int add(int a, int b);
// 定义int add(int a, int b) { return a + b;}
// 调用int sum = add(3, 5);- 函数须先声明再调用(或定义写在调用之前)
void func(void)表示无参数;void func()在 C 中表示参数未声明(旧式,不推荐)
参数传递
C 语言只有传值调用:形参是实参的副本。若需修改实参,须传指针。
void swap_wrong(int a, int b) { /* 无法交换调用者的变量 */ }
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t;}返回值
- 用
return返回;void函数无返回值 - 不要返回指向局部非静态变量的指针(函数返回后栈帧销毁,成为悬空指针)
栈帧的创建与销毁(概念)
函数每次被调用时,通常在栈上分配一帧(stack frame),存放:
- 返回地址
- 保存的寄存器(如帧指针 EBP/RBP、栈顶 ESP/RSP,具体名称依架构而定)
- 局部变量、临时空间
- 参数(调用约定决定参数在栈上还是寄存器)
调用过程(简化):
- 调用方把参数入栈(或放寄存器)
- 跳转到被调函数,保存旧帧指针,建立新帧指针
- 为局部变量分配栈空间
- 执行函数体
- 恢复寄存器与栈指针,返回到调用方
函数返回后,该帧内存可被下一次调用覆盖,故局部变量地址不可长期持有。
静态函数
static int helper(void) { return 0; }static 函数仅在当前翻译单元(.c 文件)内可见,避免与其他文件的同名函数链接冲突。
递归
- 函数直接或间接调用自身
- 必须有终止条件,否则无限递归导致栈溢出
- 适合问题可分解为同结构子问题(如阶乘、遍历树)
int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);}函数指针
int add(int a, int b) { return a + b; }
int (*fp)(int, int); // 声明:fp 是指向函数的指针fp = add; // 或 fp = &add;int r = fp(3, 5); // 通过指针调用声明格式:*返回类型 (指针名)(参数类型列表)
指针函数
函数的返回值是指针类型:
int *make_array(int n); // 返回 int*返回值不能指向已销毁的局部变量;可返回静态存储、堆内存或调用者提供的缓冲区。
回调函数
将函数指针作为参数传入,在特定时机由被调方调用,实现「机制与策略」分离。
typedef int (*callback_t)(int);
int process(int x, callback_t cb) { return cb(x);}常用于事件驱动、库扩展点;执行顺序由事件触发决定,而非写死的调用顺序。
相关概念
- 事件驱动:程序等待事件(输入、定时器等)再响应,而非纯顺序执行
- 异步:发起操作后不阻塞等待完成,完成后通过回调等通知(C 中常配合线程或 I/O 多路复用)
内联函数 inline
static inline int max(int a, int b) { return a > b ? a : b;}建议将 inline 函数放在头文件中并配合 static,避免多重定义。
- 编译器可能把函数体展开到调用处,减少调用开销
- 适合短小、频繁调用的函数;函数体过大、含复杂循环或递归时不宜内联
inline只是建议,编译器可忽略
可变参数函数
#include <stdarg.h>
int sum(int count, ...) { va_list ap; va_start(ap, count); int total = 0; for (int i = 0; i < count; i++) total += va_arg(ap, int); va_end(ap); return total;}- 至少有一个固定命名参数,用于
va_start定位可变部分 - 典型例子:
printf;类型与个数须由固定参数或约定推断,否则未定义
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐
