使用 extern 关键字也是实现跨文件函数调用的有效方法。extern 用于声明一个在其他文件中定义的函数或变量,告诉编译器"这个标识符的定义在别处,链接时会找到它"。
使用 extern 实现跨文件调用的步骤:
1. 在源文件中定义函数
// peripheral.c
#include <stdint.h>
// 实际定义底层函数
void low_level_init(void) {
// 硬件初始化代码
*((volatile uint32_t*)0x40020000) = 0x01;
}
uint32_t read_register(uint32_t reg_addr) {
return *((volatile uint32_t*)reg_addr);
}
2. 在调用方文件中使用 extern 声明函数
// driver.c
// 使用 extern 声明外部函数
extern void low_level_init(void);
extern uint32_t read_register(uint32_t reg_addr);
void driver_init(void) {
low_level_init(); // 调用外部函数
uint32_t value = read_register(0x40020800);
// 使用读取的值...
}
3. 编译链接
确保两个文件一起编译链接:
gcc -c peripheral.c -o peripheral.o
gcc -c driver.c -o driver.o
gcc peripheral.o driver.o -o firmware.elf
extern 方法与头文件方法的对比:
特性 | extern 方法 | 头文件方法 | 声明位置 | 直接在调用方源文件中声明 | 在头文件中声明 | 可维护性 | 当函数签名变更时,需要修改所有使用它的文件 | 只需修改头文件 | 代码整洁度 | 声明分散在各文件中 | 声明集中管理 | 可见性 | 可以只暴露给特定文件 | 所有包含头文件的都能访问 | 最佳实践 | 适用于小型项目或临时解决方案 | 推荐用于中大型项目 |
使用 extern 的注意事项:
正确匹配签名:
// 声明必须与定义完全匹配
extern void low_level_init(void); // 正确
extern void low_level_init(); // C中可接受但不推荐(参数不检查)
避免重复声明:
在同一个文件中不要多次声明同一个 extern 函数
与头文件结合使用(推荐做法):
// peripheral.h
#ifndef PERIPHERAL_H
#define PERIPHERAL_H
// 使用 extern 声明(可选,通常省略 extern)
void low_level_init(void);
uint32_t read_register(uint32_t reg_addr);
#endif
// driver.c
#include "peripheral.h" // 包含头文件而非直接使用 extern
void driver_init(void) {
low_level_init(); // 正常调用
}
在头文件中,函数声明前的 extern 关键字是可选的,因为函数默认具有外部链接属性。
|