为什么 main(int argc, char* argv[]) 有两个参数?

Why does main(int argc, char* argv[]) take two argument?

我一直认为 argc 需要标记 argv 的结尾,但我只是根据定义才知道 argv[argc] == NULL。我是否认为 argc 完全多余?如果是这样,我一直认为 C 以效率的名义消除了冗余。我的假设是错误的还是这背后有历史原因?如果是历史原因,能否详细说明一下?

历史。

Harbison & Steel(第 5 版,9.9 "The main program")描述如下:

Standard C requires that argv[argc] be a null pointer, but it is not so in some older implementations.

这是历史。

在早于 C 的第一版 UNIX 中,exec 将文件名和指向由 NULL 指针终止的 NUL 终止参数字符串的指针列表的地址作为参数。来自手册页:

sys exec; name; args      / exec = 11.
name: <...[=10=]>
...
args: arg1; arg2; ...; 0
arg1: <...[=10=]>
...

内核对参数进行计数,并在堆栈顶部为新图像提供参数计数,后跟指向参数字符串副本的指针列表。来自手册页:

sp--> nargs
      arg1
      ...
      argn

arg1: <arg1[=11=]>
...
argn: <argn[=11=]>

(内核源代码是here;我没有看内核是否真的在指向最后一个参数的指针之后写了一些东西。)

从第 6 版开始,exec、execl 和 execv 的文档开始注意到内核在 arg 指针之后放置了一个 -1。手册页说:

Argv is not directly usable in another execv, since argv[argc] is -1 and not 0.

在这一点上,您可能会争辩说 argc 是多余的,但是一段时间以来,程序一直在使用它,而不是查看 -1 的参数列表。例如,这里是 cal.c 的开头:

main(argc, argv)
char *argv[];
{
    if(argc < 2) {
        printf("usage: cal [month] year\n");
        exit();
    }

在第 7 版中,exec 被更改为在参数字符串之后添加一个 NULL 指针,然后是指向环境字符串的指针列表和另一个 NULL。手册页说:

Argv is directly usable in another execv because argv[argc] is 0.