1. 函数基础回顾与核心概念
在C语言中,函数是程序的基本构建块,它封装了特定功能的代码片段。一个典型的函数定义包含以下部分:
c复制返回类型 函数名(参数列表) {
// 函数体
return 返回值;
}
函数声明(也称为函数原型)则告诉编译器函数的接口信息,通常放在头文件中:
c复制返回类型 函数名(参数类型列表);
重要提示:函数声明中的参数名可以省略,但类型必须保留。良好的编程习惯是在声明中也包含参数名,提高代码可读性。
1.1 函数参数传递机制
C语言中参数传递默认采用值传递方式,这意味着函数内部操作的是参数的副本。对于需要修改原变量的情况,需要使用指针:
c复制void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
数组作为参数时,实际上传递的是数组首元素的地址。以下两种声明方式是等价的:
c复制void func(int arr[]);
void func(int *arr);
1.2 返回值处理要点
函数可以返回基本类型、指针、结构体等。返回局部变量的指针是常见错误:
c复制// 错误示例:返回局部数组的指针
char *badFunc() {
char str[] = "dangerous";
return str; // 局部数组在函数返回后被销毁
}
// 正确做法:使用静态变量或动态内存
char *goodFunc() {
static char str[] = "safe";
return str;
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 变量的作用域与存储类别
2.1 auto与register变量
auto是默认的存储类别,通常省略不写。register建议编译器将变量存储在寄存器中,但现代编译器优化已很少需要手动指定:
c复制void example() {
auto int x; // 等同于 int x
register int counter; // 建议存储在寄存器
}
2.2 static变量的独特性质
static关键字在不同上下文中有不同含义:
- 在函数内部:使局部变量具有静态存储期,生命周期延长到整个程序运行期间
c复制void counter() {
static int count = 0; // 只初始化一次
count++;
printf("%d\n", count);
}
- 在文件作用域:限制变量/函数仅在当前文件内可见
c复制static int hiddenVar; // 只在当前.c文件中可见
static void internalFunc() { // 只在当前文件中可调用
// ...
}
2.3 extern与多文件编程
extern用于声明在其他文件中定义的变量或函数:
c复制// file1.c
int globalVar = 42;
// file2.c
extern int globalVar; // 使用file1.c中定义的变量
实际经验:大型项目中,建议在头文件中声明extern变量,在对应的.c文件中定义它们。
3. 函数与数组的高级应用
3.1 多维数组作为参数
传递二维数组时,必须指定除第一维外的所有维度:
c复制void printMatrix(int mat[][3], int rows) {
for(int i=0; i<rows; i++) {
for(int j=0; j<3; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
}
3.2 变长数组(VLA)参数
C99支持变长数组参数,但需要注意兼容性问题:
c复制void processVLA(int rows, int cols, int arr[rows][cols]) {
// 处理变长二维数组
}
3.3 函数指针与回调
函数指针允许将函数作为参数传递:
c复制int compare(int a, int b) {
return a - b;
}
void sort(int *arr, int n, int (*cmp)(int, int)) {
// 使用cmp函数指针进行比较
// ...
}
// 调用
sort(array, 10, compare);
4. 常见问题与调试技巧
4.1 段错误(Segmentation Fault)排查
函数相关的常见段错误原因:
- 解引用空指针
- 数组越界访问
- 返回局部变量的指针
- 栈溢出(递归太深)
调试方法:
- 使用gdb逐步执行
- 添加打印语句
- 使用valgrind检测内存问题
4.2 链接错误解决
常见链接错误及解决方案:
| 错误类型 | 原因 | 解决方法 |
|---|---|---|
| undefined reference | 函数未定义 | 检查是否实现或链接了对应.o文件 |
| multiple definition | 重复定义 | 使用static限制作用域或正确使用extern |
| incompatible types | 声明不匹配 | 确保头文件与实现一致 |
4.3 性能优化建议
- 小函数使用static inline(在头文件中)
- 避免在循环中调用复杂函数
- 对于频繁调用的简单函数,考虑使用宏替代
- 使用const修饰不会修改的参数
5. 现代C语言函数特性
5.1 _Generic选择
C11引入的_Generic可以实现简单的函数重载效果:
c复制#define print(x) _Generic((x), \
int: printInt, \
double: printDouble, \
char*: printString)(x)
void printInt(int i) { printf("%d", i); }
void printDouble(double d) { printf("%f", d); }
void printString(char *s) { printf("%s", s); }
5.2 内联函数
inline关键字建议编译器内联展开函数:
c复制inline int max(int a, int b) {
return a > b ? a : b;
}
注意:inline只是建议,编译器可能忽略。适合小型、频繁调用的函数。
5.3 _Noreturn函数
C11引入_Noreturn指定函数不会返回:
c复制#include <stdnoreturn.h>
noreturn void fatalError(const char *msg) {
fprintf(stderr, "Error: %s\n", msg);
exit(EXIT_FAILURE);
}
6. 工程实践建议
6.1 函数设计原则
- 单一职责原则:一个函数只做一件事
- 合理的函数长度(建议不超过50行)
- 有意义的命名(动词+名词,如calculateAverage)
- 明确的参数检查
- 完善的错误处理
6.2 头文件组织
良好的头文件应包含:
- 防止重复包含的宏
- 必要的其他头文件包含
- 函数声明
- 相关宏定义
- 结构体/枚举定义
示例:
c复制#ifndef MYLIB_H
#define MYLIB_H
#include <stddef.h>
#define MAX_SIZE 100
typedef struct {
int x, y;
} Point;
int compute(Point p1, Point p2);
void processArray(int *arr, size_t size);
#endif
6.3 单元测试框架
简单测试框架示例:
c复制#define TEST(expr) \
do { \
if(!(expr)) { \
fprintf(stderr, "Test failed at %s:%d: %s\n", \
__FILE__, __LINE__, #expr); \
return 1; \
} \
} while(0)
int test_add() {
TEST(add(2, 3) == 5);
TEST(add(-1, 1) == 0);
return 0;
}
7. 函数与内存管理
7.1 动态内存分配
函数中常用的内存操作:
c复制int *createIntArray(size_t n) {
int *arr = malloc(n * sizeof(int));
if(!arr) {
perror("Memory allocation failed");
return NULL;
}
return arr;
}
void freeArray(int **arr) {
free(*arr);
*arr = NULL; // 避免悬垂指针
}
7.2 资源获取即初始化(RAII)模式
C中模拟RAII的惯用法:
c复制#define SCOPE(type, var, init, cleanup) \
for(type var = init, *_done = NULL; \
!_done; \
cleanup, _done = (void*)1)
void fileExample() {
SCOPE(FILE*, f, fopen("data.txt", "r"), fclose(f)) {
if(!f) break;
// 使用文件
char buf[100];
fgets(buf, sizeof(buf), f);
printf("%s", buf);
}
}
8. 递归函数深度解析
8.1 递归三要素
- 基准条件(递归终止条件)
- 递归调用(向基准条件推进)
- 工作处理(当前层需要做的操作)
经典示例:斐波那契数列
c复制int fibonacci(int n) {
if(n <= 1) return n; // 基准条件
return fibonacci(n-1) + // 递归调用
fibonacci(n-2); // 递归调用
}
8.2 尾递归优化
符合尾递归形式的函数可以被编译器优化为循环:
c复制int factorial(int n, int acc) {
if(n <= 1) return acc;
return factorial(n-1, n*acc); // 尾递归调用
}
// 调用
int result = factorial(5, 1);
注意:并非所有编译器都支持尾递归优化,关键算法不应依赖此特性。
8.3 递归与栈的关系
每次递归调用都会在调用栈上创建新的栈帧,包含:
- 返回地址
- 参数值
- 局部变量
理解这一点对调试递归函数至关重要。
9. 可变参数函数实现
9.1 stdarg.h宏使用
实现可变参数函数的基本步骤:
c复制#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;
}
// 调用
int s = sum(3, 10, 20, 30);
9.2 类型安全考虑
可变参数函数缺乏类型安全检查,常见解决方案:
- 使用格式字符串(如printf)
- 添加标记参数指示参数类型
- 传递参数个数和类型信息
9.3 实际应用示例
实现简单日志函数:
c复制void logMessage(const char *file, int line, const char *fmt, ...) {
fprintf(stderr, "[%s:%d] ", file, line);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}
#define LOG(...) logMessage(__FILE__, __LINE__, __VA_ARGS__)
10. 函数指针高级应用
10.1 函数指针数组
创建命令表处理不同操作:
c复制void cmdAdd() { /*...*/ }
void cmdSub() { /*...*/ }
void cmdMul() { /*...*/ }
typedef void (*Command)();
Command commands[] = {cmdAdd, cmdSub, cmdMul};
void executeCommand(int cmd) {
if(cmd >= 0 && cmd < sizeof(commands)/sizeof(commands[0])) {
commands[cmd]();
}
}
10.2 回调函数设计模式
实现事件驱动系统:
c复制typedef void (*EventHandler)(int eventType, void *data);
struct EventSystem {
EventHandler handlers[MAX_EVENTS];
};
void registerHandler(EventSystem *sys, int eventType, EventHandler handler) {
if(eventType >= 0 && eventType < MAX_EVENTS) {
sys->handlers[eventType] = handler;
}
}
void triggerEvent(EventSystem *sys, int eventType, void *data) {
if(sys->handlers[eventType]) {
sys->handlers[eventType](eventType, data);
}
}
10.3 动态库函数加载
运行时加载动态库函数:
c复制#include <dlfcn.h>
void *lib = dlopen("libmylib.so", RTLD_LAZY);
if(lib) {
typedef int (*MagicFunc)(int);
MagicFunc func = (MagicFunc)dlsym(lib, "doMagic");
if(func) {
int result = func(42);
// ...
}
dlclose(lib);
}
11. 内联汇编与函数优化
11.1 基本内联汇编语法
在函数中使用汇编指令:
c复制int addWithASM(int a, int b) {
asm volatile (
"add %0, %1" // 汇编指令
: "=r"(a) // 输出操作数
: "r"(b), "0"(a) // 输入操作数
);
return a;
}
11.2 性能关键函数优化
优化热点函数的常见技术:
- 减少函数调用开销(内联小函数)
- 循环展开
- 使用寄存器变量
- 避免不必要的内存访问
11.3 编译器指令应用
使用编译器特定指令:
c复制// GCC的热点函数标记
__attribute__((hot)) void processData() {
// ...
}
// 强制内联
__attribute__((always_inline)) int calculate(int x) {
return x * x + 2 * x + 1;
}
12. 函数安全编程实践
12.1 缓冲区溢出防护
安全字符串处理函数示例:
c复制void safeCopy(char *dest, const char *src, size_t destSize) {
if(destSize == 0) return;
size_t i;
for(i = 0; i < destSize - 1 && src[i]; i++) {
dest[i] = src[i];
}
dest[i] = '\0';
}
12.2 参数验证模式
防御性编程的参数检查:
c复制int safeDivide(int *result, int a, int b) {
if(!result) return -1; // 无效指针
if(b == 0) return -2; // 除零错误
*result = a / b;
return 0; // 成功
}
12.3 安全异常处理
使用setjmp/longjmp实现异常处理:
c复制#include <setjmp.h>
jmp_buf env;
void riskyFunction() {
if(/* 错误条件 */) {
longjmp(env, 1); // 跳转到setjmp处
}
// 正常处理
}
void caller() {
if(setjmp(env) == 0) {
riskyFunction();
} else {
// 错误处理
}
}
13. 多线程环境中的函数
13.1 线程安全函数设计
确保函数线程安全的方法:
- 避免使用静态局部变量
- 使用互斥锁保护共享资源
- 使用线程局部存储(TLS)
c复制#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void threadSafeFunc() {
pthread_mutex_lock(&mutex);
// 临界区代码
pthread_mutex_unlock(&mutex);
}
13.2 可重入函数实现
可重入函数的关键特征:
- 不使用静态/全局变量
- 不调用不可重入函数
- 不返回指向静态数据的指针
示例:
c复制// 不可重入版本
char *timestamp() {
static char buf[20];
time_t now = time(NULL);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
return buf;
}
// 可重入版本
void timestamp_r(char *buf, size_t size) {
time_t now = time(NULL);
struct tm tm_buf;
localtime_r(&now, &tm_buf);
strftime(buf, size, "%Y-%m-%d %H:%M:%S", &tm_buf);
}
13.3 线程局部存储应用
使用__thread关键字创建线程局部变量:
c复制static __thread int threadLocalVar;
void threadFunc() {
threadLocalVar = pthread_self(); // 每个线程有自己的副本
// ...
}
14. 函数式编程风格实践
14.1 高阶函数实现
实现map函数处理数组:
c复制void map(int *arr, size_t n, int (*f)(int)) {
for(size_t i=0; i<n; i++) {
arr[i] = f(arr[i]);
}
}
int square(int x) { return x * x; }
int negate(int x) { return -x; }
// 使用
int nums[] = {1, 2, 3, 4};
map(nums, 4, square); // [1, 4, 9, 16]
map(nums, 4, negate); // [-1, -4, -9, -16]
14.2 闭包模拟技术
使用结构体模拟闭包:
c复制typedef struct {
int x;
int (*func)(struct Closure*, int);
} Closure;
int closureFunc(Closure *self, int y) {
return self->x + y;
}
Closure makeAdder(int x) {
return (Closure){.x = x, .func = closureFunc};
}
// 使用
Closure add5 = makeAdder(5);
int result = add5.func(&add5, 3); // 8
14.3 惰性求值实现
使用函数指针实现惰性求值:
c复制typedef struct {
int (*get)(void*);
void *data;
} Lazy;
int evaluate(Lazy *lazy) {
return lazy->get(lazy->data);
}
// 示例:惰性加法
typedef struct {
Lazy *a;
Lazy *b;
} AddData;
int addGet(void *data) {
AddData *d = data;
return evaluate(d->a) + evaluate(d->b);
}
Lazy lazyAdd(Lazy *a, Lazy *b) {
AddData *data = malloc(sizeof(AddData));
data->a = a;
data->b = b;
return (Lazy){.get = addGet, .data = data};
}
15. 函数设计模式与架构
15.1 策略模式实现
使用函数指针实现策略模式:
c复制typedef struct {
void (*sort)(int *, size_t);
} SortStrategy;
void bubbleSort(int *arr, size_t n) { /*...*/ }
void quickSort(int *arr, size_t n) { /*...*/ }
void processData(int *data, size_t size, SortStrategy strategy) {
// 预处理...
strategy.sort(data, size);
// 后处理...
}
// 使用
SortStrategy strategy = {.sort = quickSort};
processData(array, 100, strategy);
15.2 观察者模式实现
基于回调的事件通知:
c复制typedef void (*Observer)(const char *event, void *data);
typedef struct {
Observer observers[10];
size_t count;
} Subject;
void subscribe(Subject *subj, Observer obs) {
if(subj->count < 10) {
subj->observers[subj->count++] = obs;
}
}
void notify(Subject *subj, const char *event, void *data) {
for(size_t i=0; i<subj->count; i++) {
subj->observers[i](event, data);
}
}
15.3 状态机实现
函数指针状态机:
c复制typedef struct {
void (*currentState)(struct StateMachine*);
} StateMachine;
void stateA(StateMachine *sm) {
// 处理状态A逻辑
sm->currentState = stateB; // 转移状态
}
void stateB(StateMachine *sm) {
// 处理状态B逻辑
sm->currentState = stateA; // 转移状态
}
void runMachine(StateMachine *sm) {
while(1) {
sm->currentState(sm);
}
}
16. 函数调试与性能分析
16.1 函数调用跟踪
使用宏实现调用跟踪:
c复制#define ENTER() \
printf("--> %s\n", __func__)
#define EXIT() \
printf("<-- %s\n", __func__)
void example() {
ENTER();
// 函数逻辑...
EXIT();
}
16.2 性能剖析技术
使用clock()测量函数执行时间:
c复制#include <time.h>
void profileMe() {
clock_t start = clock();
// 被测函数代码...
clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("Function took %.6f seconds\n", elapsed);
}
16.3 代码覆盖率分析
使用GCC的覆盖率选项:
bash复制gcc -fprofile-arcs -ftest-coverage -o program program.c
./program
gcov program.c
17. 跨平台函数开发
17.1 条件编译处理差异
处理平台相关代码:
c复制#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif
DLL_EXPORT void crossPlatformFunc() {
// 平台无关代码...
#ifdef _WIN32
// Windows特有实现
#else
// Unix特有实现
#endif
}
17.2 可移植类型定义
使用标准类型保证可移植性:
c复制#include <stdint.h>
void portableFunc() {
int32_t guaranteed32bit; // 确保32位有符号整数
uint64_t unsigned64bit; // 确保64位无符号整数
size_t sizeType; // 适合表示大小的类型
}
17.3 ABI兼容性考虑
确保函数接口二进制兼容:
- 避免更改已发布函数的签名
- 使用稳定的数据类型
- 考虑结构体填充和对齐
- 使用版本控制机制
18. 函数文档与注释规范
18.1 Doxygen文档标准
符合Doxygen的注释示例:
c复制/**
* @brief 计算两个数的最大公约数
*
* @param a 第一个整数
* @param b 第二个整数
* @return int 最大公约数
*
* @note 使用欧几里得算法实现
* @warning 参数不能同时为0
*/
int gcd(int a, int b);
18.2 自文档化代码技巧
通过命名和结构提高可读性:
c复制// 不好的写法
int f(int x, int y) {
return x > y ? x : y;
}
// 好的写法
int maximumOfTwoIntegers(int firstNumber, int secondNumber) {
return (firstNumber > secondNumber) ? firstNumber : secondNumber;
}
18.3 注释最佳实践
有效的注释原则:
- 解释为什么(why),而不是做什么(what)
- 避免无意义的注释
- 标记TODO/FIXME等需要改进的地方
- 保持注释与代码同步更新
19. 函数版本控制策略
19.1 语义化版本控制
函数库版本管理示例:
c复制// v1.0.0
int oldFunction(int param);
// v2.0.0 (破坏性变更)
int newFunction(int param, int options);
// 保持兼容性
#ifdef USE_NEW_API
#define myFunction newFunction
#else
#define myFunction oldFunction
#endif
19.2 弃用函数标记
使用属性标记弃用函数:
c复制#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED __declspec(deprecated)
#else
#define DEPRECATED
#endif
DEPRECATED void oldAPI();
19.3 ABI稳定技术
保持二进制兼容性的技巧:
- 添加新参数时放在末尾
- 使用不透明指针隐藏实现细节
- 通过版本参数扩展功能
- 提供适配层处理不同版本
20. 函数测试与验证
20.1 单元测试框架集成
简单测试框架扩展:
c复制#define TEST_CASE(name) \
void test_##name(); \
__attribute__((constructor)) void register_##name() { \
add_test(test_##name, #name); \
} \
void test_##name()
struct TestCase {
void (*func)();
const char *name;
struct TestCase *next;
};
void run_tests() {
struct TestCase *test = test_list;
while(test) {
printf("Running %s...", test->name);
test->func();
printf(" PASS\n");
test = test->next;
}
}
20.2 模糊测试(Fuzzing)应用
简单模糊测试实现:
c复制void fuzzTest() {
unsigned seed = time(NULL);
srand(seed);
for(int i=0; i<1000; i++) {
int a = rand();
int b = rand();
testFunction(a, b); // 被测函数
}
}
20.3 静态分析工具集成
使用编译器静态分析:
c复制// GCC特定属性
void checkedFunction() __attribute__((nonnull(1)));
void checkedFunction(int *ptr) {
// 编译器会检查ptr是否为NULL
}
21. 函数优化高级技巧
21.1 循环优化技术
优化热点循环的常见方法:
c复制// 原始版本
void processArray(int *arr, int n) {
for(int i=0; i<n; i++) {
arr[i] = complexCalculation(arr[i]);
}
}
// 优化版本:循环展开
void processArrayOpt(int *arr, int n) {
int i;
for(i=0; i+3<n; i+=4) {
arr[i] = complexCalculation(arr[i]);
arr[i+1] = complexCalculation(arr[i+1]);
arr[i+2] = complexCalculation(arr[i+2]);
arr[i+3] = complexCalculation(arr[i+3]);
}
for(; i<n; i++) {
arr[i] = complexCalculation(arr[i]);
}
}
21.2 分支预测优化
帮助CPU分支预测的提示:
c复制// 可能为真的条件
#define likely(x) __builtin_expect(!!(x), 1)
// 可能为假的条件
#define unlikely(x) __builtin_expect(!!(x), 0)
void criticalPath(int x) {
if(unlikely(x < 0)) {
handleError();
} else {
// 主要处理路径
}
}
21.3 数据局部性优化
改善缓存利用率的技巧:
c复制// 不好的写法:跳跃访问
void processMatrixBad(int **mat, int n) {
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
mat[j][i] = process(mat[j][i]); // 列优先访问
}
}
}
// 好的写法:连续访问
void processMatrixGood(int **mat, int n) {
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
mat[i][j] = process(mat[i][j]); // 行优先访问
}
}
}
22. 函数与硬件特性
22.1 SIMD指令应用
使用编译器内置函数实现向量化:
c复制#include <immintrin.h>
void vectorAdd(float *a, float *b, float *c, int n) {
for(int i=0; i<n; i+=4) {
__m128 va = _mm_load_ps(&a[i]);
__m128 vb = _mm_load_ps(&b[i]);
__m128 vc = _mm_add_ps(va, vb);
_mm_store_ps(&c[i], vc);
}
}
22.2 内存屏障使用
多线程编程中的内存顺序控制:
c复制#include <stdatomic.h>
void publishMessage(Message *msg) {
// 准备消息
msg->content = "Hello";
msg->length = 5;
// 写入屏障确保前面的写入对其他线程可见
atomic_thread_fence(memory_order_release);
msg->ready = 1; // 发布标志
}
22.3 缓存行对齐
避免伪共享(false sharing):
c复制struct AlignedData {
_Alignas(64) int thread1Data; // 64字节对齐(典型缓存行大小)
_Alignas(64) int thread2Data;
};
void accessData(struct AlignedData *data) {
// 不同线程可以安全访问不同字段
}
23. 函数与编译器特性
23.1 属性扩展应用
常用GCC函数属性:
c复制// 不会返回的函数
__attribute__((noreturn)) void fatalError();
// 纯函数(无副作用)
__attribute__((pure)) int calculate(int x);
// 常量函数(相同输入总是相同输出)
__attribute__((const)) int square(int x);
// 冷热函数标记
__attribute__((cold)) void errorHandler();
__attribute__((hot)) void processData();
23.2 链接器脚本控制
控制函数在内存中的布局:
ld复制MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS {
.critical_code : {
*(.critical_functions)
} > FLASH AT> FLASH
}
对应的函数声明:
c复制#define CRITICAL __attribute__((section(".critical_functions")))
CRITICAL void timeCriticalFunc();
23.3 编译时断言
静态检查函数前提条件:
c复制#define STATIC_ASSERT(cond, msg) \
typedef char static_assert_##msg[(cond)?1:-1]
void processBuffer(char *buf, int size) {
STATIC_ASSERT(sizeof(int) == 4, int_size_check);
// ...
}
24. 函数设计哲学
24.1 UNIX设计原则
遵循UNIX哲学的函数设计:
- 每个函数只做一件事
- 函数之间通过简单接口通信
- 优先使用文本流作为接口
- 尽早构建原型并迭代
24.2 正交性原则
保持函数正交性的技巧:
- 最小化函数间的依赖
- 避免全局状态
- 明确分离命令与查询
- 使用回调处理可变行为
24.3 最小惊讶原则
设计符合直觉的函数接口:
- 遵循领域惯例
- 保持参数顺序一致
- 使用标准错误处理模式
- 避免隐式行为
25. 函数与算法实现
25.1 常用算法封装
通用排序函数实现:
c复制typedef int (*Comparator)(const void*, const void*);
void genericSort(void *base, size_t nmemb, size_t size, Comparator cmp) {
for(size_t i=0; i<nmemb-1; i++) {
for(size_t j=0; j<nmemb-i-1; j++) {
void *a = (char*)base + j*size;
void *b = (char*)base + (j+1)*size;
if(cmp(a, b) > 0) {
swap(a, b, size);
}
}
}
}
void swap(void *a, void *b, size_t size) {
char tmp[size];
memcpy(tmp, a, size);
memcpy(a, b, size);
memcpy(b, tmp, size);
}
25.2 算法选择策略
根据场景选择合适算法:
- 小数据量:简单算法(如插入排序)
- 大数据量:O(nlogn)算法(如快速排序)
- 特定数据:专用算法(如计数排序)
- 稳定需求:保持相等元素顺序(如归并排序)
25.3 内存与性能权衡
空间换时间示例:
c复制// 预先计算阶乘表
static const int FACTORIALS[] = {
1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
};
int factorial(int n) {
if(n >=0 && n < sizeof(FACTORIALS)/sizeof(FACTORIALS[0])) {
return FACTORIALS[n]; // O(1)时间查找
}
return -1; // 错误值
}
26. 函数与数据结构
26.1 通用容器接口
设计通用数据结构操作函数:
c复制typedef struct {
void *(*allocate)(size_t);
void (*deallocate)(void*);
void (*insert)(void *container, void *data);
void *(*find)(const void *container, const void *key);
} DataStructureOps;
void processWithDS(DataStructureOps ops) {
void *ds = ops.allocate(100);
int data = 42;
ops.insert(ds, &data);
// ...
ops.deallocate(ds);
}
26.2 迭代器模式实现
通用迭代器接口:
c复制typedef struct {
void *(*begin)(void *container);
void *(*next)(void *container, void *iter);
int (*end)(void *container, void *iter);
void *(*get)(void *iter);
} Iterator;
void traverse(void *container, Iterator it) {
for(void *iter = it.begin(container);
!it.end(container, iter);
iter = it.next(container, iter)) {
void *data = it.get(iter);
// 处理数据...
}
}
26.3 内存池优化
自定义内存分配函数:
c复制typedef struct {
char *pool;
size_t size;
size_t used;
} MemoryPool;
void *poolAlloc(MemoryPool *pool, size_t size) {
if(pool->used + size > pool->size) {
return NULL; // 空间不足
}
void *ptr = pool->pool + pool->used;
pool->used += size;
return ptr;
}
void poolFree(MemoryPool *pool) {
pool->used = 0; // 简单重置
}
27. 函数与系统编程
27.1 系统调用封装
安全封装系统调用模式:
c复制int safeRead(int fd, void *buf, size_t count) {
ssize_t n;
do {
n
