接手他人代碼常見問題:所有邏輯塞在 main.c、頭文件濫用、變量重複定義、難以定位功能、維護成本極高。規範的多文件結構是團隊協作與長期維護的基石。
按功能內聚劃分模塊,每模塊一對 .c/.h:
main.c/.h:系統初始化、主循環、全局狀態標志位。uart.c/.h:串口/I2C 通信驅動、myprintf 等工具函數。key.c/.h:按鍵/撥碼開關掃描、去抖動。flash.c/.h:Flash 讀寫封裝。battery.c/.h:電池管理(ADC 採樣、電量估算)。power.c/.h:電源控制邏輯(開關機時序、保護)。PROJECT/
├── PROJ/ # 工程文件
├── APP/
│ ├── main.c
│ ├── main.h
│ └── aplication.c
├── BASEDRIVE/
│ ├── uart.c
│ └── uart.h
├── EXTIDRIVE/
│ ├── flash.c
│ └── flash.h
└── DOC/
└── Readme.md
$PROJ_DIR$/../BASEDRIVE 等相對路徑。之後在代碼中直接 #include "uart.h" 即可,無需寫路徑。
.c 內部。.h 用 extern 聲明。.h 定義。static,不在 .h 暴露。// uart.c
static uint8_t uart_rx_buf[64]; // 內部緩衝區,不對外
static uint8_t uart_rx_len = 0;
uint8_t uart_tx_busy_flag = 0; // 需外部查詢,定義在 .c
void uart_init(void) { ... } // 定義
void myprintf(const char *str) { ... }
// uart.h
#ifndef __UART_H__
#define __UART_H__
#include
extern uint8_t uart_tx_busy_flag; // 聲明
void uart_init(void); // 聲明
void myprintf(const char *str); // 聲明
#define UART_BAUDRATE 115200 // 宏定義
#endif
// 錯誤!多個 .c 包含此 .h 會導致重複定義鏈接錯誤 // uart.h uint8_t uart_tx_busy_flag = 0; // 定義!
即使加 #ifndef 防衛,也只能防止同一編譯單元重複包含,**無法防止不同 .c 編譯後鏈接時符號重複**。
// main.c #include "main.h" #include "uart.h" #include "key.h" #include "flash.h" #include "battery.h" #include "power.h" // 全局狀態定義在 main.c,聲明在 main.h 供其他模塊訪問 system_state_t g_sys_state = STATE_IDLE;
| 項目 | 規範 | 原因 |
|---|---|---|
| 模塊劃分 | 功能內聚,一功能一 .c/.h | 職責清晰,便於並行開發與測試 |
| 目錄結構 | 按層級分目錄,配置編譯器 Include Path | 避免硬編碼路徑,支持跨平台構建 |
| 變量定義 | 僅在 .c 定義,.h 用 extern 聲明 | 防止鏈接重複定義錯誤 |
| 內部符號 | 加 static 限制作用域 | 封裝實現細節,減少命名衝突 |
| 宏定義 | 直接在 .h 定義 | 編譯期展開,無鏈接符號問題 |
| 函數聲明 | .h 聲明,.c 定義 | 接口與實現分離 |
嵌入式項目規模雖小,但「高內聚、低耦合、清晰所有權」的工程化原則同樣適用。建立規範的多文件結構,不僅為團隊協作鋪路,更是對自己未來維護工作的最大善意。