/

如何看待C語言的argv與指標關係

前言

在一次作業下碰到了 argv 與的操作,看到課本

UNIX Systems Programming: Communication, Concurrency and Threads: Communication, Concurrency and Threads (2nd Edition) 2nd Edition

對於 argv 的操作為下

1
int main(int argc, char* argv[]) { }

回想起以前常常都是使用以下這種用法

1
int main(int argc, char** argv) { }

於是開始研究了 C 語言的 argv 到底傳了什麼東西給 main function

正文

在記憶體中 argv 是一個指標,指向 char 陣列,每一陣列的內容就是不同的 argv 字串,如下圖所示。

1

圖片來源 (https://softwareengineering.stackexchange.com/questions/385819/why-is-c-c-main-argv-declared-as-char-argv-rather-than-just-char-argv)

所以當 main 在接收 argv 這個指標時,它只是一個指向字元陣列的指標,需要對它進一步操作,例如:

1
2
3
4
5
6
7
8
#include 
int main(int argc, char* argv[]) {
printf("%s\n", argv[0]); // argv指向陣列中的第0格的值
printf("%p\n", &argv[0]); // argv指向陣列中的第0格的位置
printf("%p\n", &argv[1]); // argv指向陣列中的第1格的位置
printf("%p\n", argv); // argv指標本身存的值
printf("%p\n", &argv); // argv指標本身的位置
}

編譯:
gcc app.c

output:

1
2
3
4
5
./a.out
0x7fffffffe5b8
0x7fffffffe5c0
0x7fffffffe5b8
0x7fffffffe4c0

把結果對印至上圖,如同以下:

2

如果將陣列的第二格位置與第一格相減,可以發現長度為 8

0x7fffffffe5c0 - 0x7fffffffe5b8 = 8

而這個 8 就是 ./a.out 這個字串的長度 (., /, a, ., o, u, t, \0)

結論

如此一來,兩種寫法的關係就清晰多了。

1
int main(int argc, char** argv) { }

這個寫法只不過是 pointer to pointer 的概念,與另外一種可以說是沒有分別。

不相信的話可以做個小實驗:

1
2
3
4
5
6
7
8
#include 
int main(int argc, char** argv) {
printf("%s\n", argv[0]);
printf("%p\n", &argv[0]);
printf("%p\n", &argv[1]);
printf("%p\n", argv);
printf("%p\n", &argv);
}

編譯:
gcc app2.c

output:

1
2
3
4
5
./a.out
0x7fffffffe5b8
0x7fffffffe5c0
0x7fffffffe5b8
0x7fffffffe4c0

與上述第一種寫法完全相同!

(實際上 OS 有一種 ASLR 的機制讓每一次執行程式的 virtual memory 都不同,所以做這個實驗時,記得先暫時關閉 ASLR。)

所以依照個人習慣,想要用哪一種寫法都可以 👏