| 術語 | 定義 | 別名 | 作用域 | 生命週期 |
|---|---|---|---|---|
| 內部變數 | 函數內部定義 | 區域變數/自動變數 | 僅該函數內 | 函數呼叫時建立,返回時銷毀 |
| 外部變數 | 函數外部定義 | 全域變數 | 整個程式(跨檔案需extern) | 程式啟動至結束,永久存在 |
| 靜態區域變數 | 函數內 static | 內部靜態變數 | 僅該函數內 | 永久存在,保持值跨呼叫 |
| 靜態外部變數/函數 | 檔案層級 static | 檔案域變數/函數 | 僅該來源檔 | 永久存在 |
| 函數 | 不可巢狀定義 | 全域函數 | 整個程式(跨檔案需extern) | 程式期間永久存在 |
預設情況下,外部變數與函數具有外部連結:所有檔案透過名稱引用同一實體。這使跨函數資料共享成為可能,但也可能造成過度耦合。
/* global.c - 唯一定義點 */
int VarDesc;
char Array[MAXVAL];
double rates[100];
/* global.h - 供所有檔案包含 */
#ifndef _GLOBAL_H
#define _GLOBAL_H
#ifdef __cplusplus
extern "C" {
#endif
extern int VarDesc;
extern char Array[]; // 陣列大小在定義處決定
extern double rates[];
#ifdef __cplusplus
}
#endif
#endif
#include "global.h"。/* global.h - 介面宣告 */
#ifndef _GLOBAL_H
#define _GLOBAL_H
#ifdef __cplusplus
extern "C" {
#endif
extern int g_system_state;
extern char g_config_buffer[256];
extern void system_init(void);
extern int process_data(int input);
#ifdef __cplusplus
}
#endif
#endif
/* global.c - 實作與定義 */
#include "global.h"
#include <string.h>
int g_system_state = 0;
char g_config_buffer[256] = {0};
void system_init(void) {
g_system_state = 1;
memset(g_config_buffer, 0, sizeof(g_config_buffer));
}
int process_data(int input) {
return input * 2;
}
/* user.c - 使用者模組 */
#include "global.h"
void user_task(void) {
if (g_system_state) {
int result = process_data(42);
// ...
}
}
extern 可省略,但建議保留以明確語意:
/* func.h */
extern int calculate(int a, int b); // 明確宣告
// int calculate(int a, int b); // 也合法,但不明確
/* func.c */
#include "func.h"
int calculate(int a, int b) { // 定義
return a + b;
}
func.c 定義func.h(加 extern)#include "func.h"cc func.c user.c 或分別編譯再連結/* module.c */
#include "module.h"
static int internal_counter = 0; // 僅本檔可見
static void internal_helper(void) { ... } // 僅本檔可呼叫
void public_api(void) { // 對外介面
internal_counter++;
internal_helper();
}
效果:
int get_next_id(void) {
static int id = 0; // 僅初始化一次,跨呼叫保持值
return ++id;
}
void reset_id(void) {
static int *p = NULL; // 指向靜態變數的指標
if (!p) p = (int*)(&id); // 技巧:取得靜態區域變數位址(僅示意)
*p = 0;
}
對比自動變數:
| 特性 | 自動變數 | 靜態區域變數 |
|---|---|---|
| 儲存位置 | 堆疊 | 資料段/ BSS 段 |
| 初始化時機 | 每次進入函數 | 程式啟動前(僅一次) |
| 值的保持 | 不保持 | 跨函數呼叫保持 |
| 執行緒安全 | 各執行緒獨立 | 共享(需同步機制) |
project/
├── include/
│ ├── global.h // 系統級全域介面
│ ├── uart.h // UART 模組介面
│ ├── lcd.h // LCD 模組介面
│ └── timer.h // 定時器模組介面
├── src/
│ ├── global.c // 全域變數定義
│ ├── main.c // 進入點
│ ├── uart.c // UART 實作(含 static 內部函數)
│ ├── lcd.c // LCD 實作
│ └── timer.c // 定時器實作
├── Makefile
└── README.md
static inline 或 const 整數常數)。#ifndef/_#define/_#endif)。extern "C" 包裹 C 標頭檔。global.c/h。-Wall -Wextra),連結時檢查未定義/重複定義。理解「定義vs宣告」、「外部vs靜態連結」、「作用域vs生命週期」是C語言模組化程式設計的基石。良好的模組劃分:標頭檔定義介面(宣告),來源檔實作細節(定義+static封裝),能構建清晰、可維護、可擴展的大型C專案。這正是從「寫程式」進化為「軟體工程」的關鍵一步。