環境變量是進程與外部配置解耦的關鍵機制。本文以 Turbo C 時代的底層實現為切入點,講解 getenv/putenv 標準庫用法,並延伸至現代跨平台開發的安全模式與常見坑點。
#include <stdlib.h>
char *getenv(const char *name); // 讀取,返回指向環境字串的指針(不可修改)
int putenv(char *string); // 設置/修改,格式 "NAME=VALUE"
int setenv(const char *name, const char *value, int overwrite); // POSIX,更安全
int unsetenv(const char *name); // POSIX,刪除環境變量
// 用戶在 DOS/Shell 執行:set MOUSE=YES
void load_mouse_driver() {
char *p = getenv("MOUSE");
if (p && strcmp(p, "YES") == 0) {
// 裝載鼠標驅動...
} else {
printf("No mouse configured.\n");
}
}
早期 DOS 無標準庫封裝,需直接掃描 PSP(Program Segment Prefix)指向的環境段:
peek(_psp, 0x2C) 讀取環境段地址。NAME=VALUE\0 字串組成,以雙 \0\0 結尾。NAME=VALUE 對。代碼片段(僅供理解原理,現代不可用):
int find_env_string(int restart) {
static int env_offset = 0;
static unsigned environment = 0;
if (!environment) environment = peek(_psp, 0x2C);
if (restart) { environment = 0; env_offset = 0; } // 重頭開始
int buf_offset = 0;
while (1) {
char ch = peekb(environment, env_offset++);
env_string[buf_offset] = ch;
if (ch) { buf_offset++; continue; } // 非 \0 繼續讀
if (!buf_offset) return 0; // 雙 \0 結束
env_string[buf_offset] = '\0';
return 1; // 找到一個完整環境串
}
}
配置應在啟動腳本/容器/系統服務中注入,程序只讀取。避免 putenv/setenv 導致多執行緒競態、內存洩漏(glibc 實現會分配新字串)。
GetEnvironmentVariableA 線程安全。getenv 在 glibc 2.24+ 線程安全,舊版本需加鎖或用 secure_getenv(僅讀)。// 封裝示例
bool get_env_bool(const char *name, bool def) {
const char *v = getenv(name);
if (!v) return def;
return (strcmp(v, "1") == 0 || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0);
}
int get_env_int(const char *name, int def) {
const char *v = getenv(name);
return v ? atoi(v) : def;
}
// ❌ 錯誤:字串字面量/棧上字串不可修改,且 putenv 可能直接存指針
putenv("PATH=/new/path"); // UB
// ✅ 正確:動態分配,程序生命周期內不釋放
char *env = malloc(strlen("PATH=/new/path") + 1);
strcpy(env, "PATH=/new/path");
putenv(env); // 由運行時管理,勿 free
| 場景 | 建議方案 |
|---|---|
| 功能開關 | getenv("FEATURE_X") + 布爾封裝 |
| 路徑配置 | getenv("MYAPP_HOME") 回退默認路徑 |
| 密鑰/敏感信息 | 禁用環境變量,改用密鑰管理服務/配置文件權限控制 |
| 子進程繼承 | 父進程 setenv 後 fork/exec 或 CreateProcess 指定環境塊 |
從 DOS 手工掃描環境段到現代 getenv/setenv,核心模型未變:**進程擁有一份鍵值對副本,子進程繼承**。現代開發應堅持「配置外部化、代碼只讀、類型安全封裝」,將環境變量作為部署時靜態注入的輕量配置通道,而非運行時動態修改的全域狀態容器。