project/ ├── main.c # 程序入口 ├── app.h # 全局聲明(extern變量、公共宏、類型定義) ├── people.h # 模塊接口聲明 ├── people.c # 模塊實現 └── Makefile / .vcxproj
#ifndef __PEOPLE_H__ // 防止重複包含 #define __PEOPLE_H__ // 函數聲明(接口契約) char* run(void); void say(const char* str); void eat(char str[]); int calculate(int a, int b); #endif // __PEOPLE_H__
關鍵點:
#ifndef/__FILENAME_H__ / #define / #endifvoid say(const char*); 同樣合法#include "people.h"
#include "app.h" // 引用全局變量聲明
#include <string.h>
#include <stdio.h>
// 訪問外部變量(需在app.h中extern聲明)
extern int distance;
char* run(void) {
distance = 100; // 修改外部變量
return "he has runned about ";
}
void say(const char* str) {
printf("我說");
while (*str) putchar(*str++);
putchar('\n');
}
void eat(char str[]) {
printf("我正在吃");
while (*str) putchar(*str++);
putchar('\n');
}
int calculate(int a, int b) {
return a * b;
}
#ifndef __APP_H__ #define __APP_H__ #include <stdio.h> // 跨文件共享變量聲明 // ⚠️ 僅聲明,不初始化!初始化在定義處進行 extern int distance; #endif // __APP_H__
#include "app.h" // 1. 先包含全局聲明(獲取extern變量聲明)
#include "people.h" // 2. 再包含模塊接口
// 定義並初始化外部變量(僅此處一次!)
int distance = 0;
int main(void) {
printf("歡迎學習模塊化編程\n");
eat("meat");
say("hello world");
char* p = run();
while (*p) putchar(*p++);
printf("%d meter!\n", distance); // 輸出 100
return 0;
}
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ app.h │ │ people.c │ │ main.c │
│ │ │ │ │ │
│ extern int │←───包含───│ #include │←───包含───│ #include │
│ distance; │ │ "app.h" │ │ "app.h" │
│ │ │ │ │ │
│ │ │ distance=100│ │ int distance│
│ │ │ (修改) │ │ = 0; (定義) │
└─────────────┘ └─────────────┘ └─────────────┘
↑ ↑ ↑
僅聲明 可讀寫 定義+初始化
(無內存分配) (引用同一地址) (分配內存)
gcc -c main.c → main.o (編譯期:看到 extern int distance; 知其存在)
gcc -c people.c → people.o (編譯期:看到 extern int distance; 知其存在)
gcc main.o people.o -o app
↑
鏈接期:解析 distance 符號,發現 main.o 中定義,
將 people.o 中對 distance 的引用綁定到該地址
| 錯誤代碼/現象 | 原因 | 正確做法 |
|---|---|---|
| multiple definition of 'distance' | 多個 .c 中定義了同名變量 | 僅在一個 .c 中定義,其他 .h 中 extern 聲明 |
| undefined reference to 'distance' | 聲明了 extern 但無定義 | 確保某個 .c 中有 int distance = 0;(無 extern) |
| 頭文件中初始化 extern 變量 | extern int x = 10; 在頭文件 | 頭文件只能 extern int x;,定義初始化放 .c 文件 |
| 函數聲明加了 extern | extern void func(); | 函數默認 extern,可省略;加上也不錯但無必要 |
| 靜態全局變量跨文件訪問 | static int x; 在 A.c,B.c 想用 | static 限制作用域,不可跨文件。去掉 static 或提供 getter 函數 |
static,不出現在 .h 中C語言模塊化編程核心在於:.h 放聲明(接口契約),.c 放定義/實現,extern 跨文件共享變量,唯一定義多處聲明。掌握這套機制,能構建清晰、可維護、可擴展的中大型 C 項目,也是嵌入式、系統級開發的必備基本功。