C 語言模組化程式設計 (.h 與 .c 檔案)
當程式越來越大時,將所有程式碼都塞在一個 main.c 裡會變成一場災難。為了好維護、好分工,我們會將程式拆成多個檔案。這就是模組化 (Modular Programming)。
為什麼要分檔?
- 可讀性:將相關的功能分門別類(例如將學生相關函式放在
student.c,數學相關放在math_utils.c)。 - 重用性:寫好的模組可以在其他專案中重複使用。
- 編譯速度:修改某個檔案時,只需要重新編譯該檔案,不用整個專案重編(這在大型專案非常重要)。
.h 與 .c 的分工
在 C 語言中,我們通常會將一個模組拆成兩個檔案:
標頭檔 Header File (.h):
- 用途:對外公開的「介面」或「說明書」。
- 內容:函式原型 (Prototypes)、結構定義 (Struct Definitions)、巨集 (Macros)、
extern變數宣告。 - 原則:只放宣告 (Declaration),不放實作 (Definition)。
原始碼檔案 Source File (.c):
- 用途:實際的「實作」或「內容」。
- 內容:函式的具體程式碼、全域變數的定義。
- 原則:必須
#include對應的 .h 檔,以確保宣告與實作一致。
實戰範例:Math 工具箱
假設我們要寫一個簡單的數學模組。
1. 建立標頭檔 my_math.h
// my_math.h
#ifndef MY_MATH_H
#define MY_MATH_H
// 宣告函式原型
int add(int a, int b);
int sub(int a, int b);
#endif
Include Guard (防護罩):
#ifndef MY_MATH_H #define MY_MATH_H ... #endif這三行非常重要!它是為了避免同一個 .h 檔被重複 include 而導致編譯錯誤。
2. 建立實作檔 my_math.c
// my_math.c
#include "my_math.h" // 引用自己的標頭檔
// 實作函式
int add(int a, int b) {
return a + b;
}
int sub(int a, int b) {
return a - b;
}
3. 主程式 main.c
// main.c
#include <stdio.h>
#include "my_math.h" // 引用標頭檔,這樣 main 才知道 add 和 sub 長怎樣
int main() {
printf("10 + 20 = %d\n", add(10, 20));
printf("10 - 20 = %d\n", sub(10, 20));
return 0;
}
編譯多個檔案
由於現在我們有多個 .c 檔,編譯時必須將它們一起編譯。
# 同時編譯主程式和模組
gcc main.c my_math.c -o my_program
# 執行
./my_program
extern 與 static 關鍵字
在多檔案開發中,這兩個關鍵字很重要:
- extern:告訴編譯器「這個變數是在別的檔案定義的,請去別的地方找」。通常用於 .h 檔中宣告全域變數。
- static:
- 用在全域變數或函式前:表示這個變數/函式只能在這個 .c 檔內被看到(私有化),避免與其他檔案的變數名稱衝突。
建立標頭檔 shared_data.h
#ifndef SHARED_DATA_H
#define SHARED_DATA_H
// extern: 宣告一個全域變數,告訴編譯器這個變數在別的檔案中定義
extern int global_counter;
// 宣告一個函式原型
void increment_and_print_counter();
#endif
建立實作檔 shared_data.c
#include <stdio.h>
#include "shared_data.h"
// 定義 global_counter,這是它實際分配記憶體的地方
int global_counter = 0;
// static: 宣告一個靜態函式,它只能在 shared_data.c 這個檔案內被呼叫
static void _internal_log_message(const char* msg) {
printf("[Internal] %s\n", msg);
}
void increment_and_print_counter() {
_internal_log_message("Incrementing counter...");
global_counter++;
printf("Counter in shared_data.c: %d\n", global_counter);
}
建立主程式 main.c
#include <stdio.h>
#include "shared_data.h" // 引入標頭檔,才能使用 extern 宣告的變數和函式
// 這裡不能直接呼叫 _internal_log_message(),因為它是 static 的,只在 shared_data.c 內部可見
int main() {
printf("Initial global_counter from main.c: %d\n", global_counter); // 使用 extern 變數
global_counter = 10; // 修改 extern 變數
printf("Modified global_counter from main.c: %d\n", global_counter);
increment_and_print_counter(); // 呼叫 shared_data.c 中的函式
increment_and_print_counter();
printf("Final global_counter from main.c: %d\n", global_counter);
return 0;
}
掌握模組化設計,是從寫「作業」進階到寫「專案」的關鍵一步!