對於 C/C++ 程式設計師,int main(int argc, char **argv) 是再熟悉不過的簽名。但你是否真正了解作業系統如何準備這些參數?argv 到底指向哪裡?環境變數又藏在哪?本文透過實驗程式碼,帶你一探究竟。
argv 指向的字串內容。char *argv[],元素個數至少為 argc,但實際上核心還會在其後附加環境變數指標(如 HOSTNAME, PATH, TERM 等),以 NULL 指標結尾。核心依據空格將命令列分割為多個參數,將各參數的首地址存入 argv[i]。為了讓 printf("%s", argv[i]) 正常運作,核心會將原本分隔參數的空格替換為 \0(即 *(argv[i]-1) = 0),使每個參數成為獨立的 C 風格字串。
編譯執行以下程式:
/* argcargv.c */
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
int i;
for (i = 0; i < argc; i++) {
printf("%p,%s$\n", argv[i], argv[i]);
}
printf("\n");
// 修改 argv[0]
strcpy(argv[0], "meilidezhongguo,nihao!");
for (i = 0; i < argc; i++) {
printf("%p,%s$\n", argv[i], argv[i]);
}
return 0;
}
執行命令:./a.out abc 123 def 456 hello 789 world
修改前:
0xbff64758,./a.out$ 0xbff64760,abc$ 0xbff64764,123$ 0xbff64768,def$ 0xbff6476c,456$ 0xbff64770,hello$ 0xbff64776,789$ 0xbff6477a,world$ (nil),(null)$ ...... (環境變數指標)
觀察位址:argv[0] 到 argv[7] 連續分佈,間隔即為字串長度+1(含結尾 \0)。argv[8] 為 NULL,隨後是環境變數指標。
修改後:
0xbff64758,meilidezhongguo,nihao!$ 0xbff64760,hongguo,nihao!$ 0xbff64764,guo,nihao!$ 0xbff64768,nihao!$ 0xbff6476c,o!$ 0xbff64770,hello$ 0xbff64776,789$ 0xbff6477a,world$
strcpy 將較長的字串寫入 argv[0] 起始的緩衝區,導致後續參數的記憶體被覆蓋,印出時顯示為被截斷的尾部。這證明了:
argv 指向的記憶體是連續、可寫的。argv[0] 會影響相鄰參數的內容,實務上極不建議這樣做,除非你確定緩衝區足夠大(如使用 strncpy 限制長度)。environ 全域變數(或 main 第三參數 char **envp)正式提供環境變數存取。argv[argc] == NULL,方便遍歷。理解 argc/argv 的底層機制,不僅能滿足好奇心,更有助於編寫健壯的命令列解析邏輯,避免緩衝區溢位等安全隱患。