Почему main (int argc, char * argv []) принимает два аргумента?

Я всегда думал, что argc должен был пометить конец argv, но я только что узнал, что argv[argc] == NULL по определению. Правильно ли я думаю, что argc полностью избыточно? Если это так, я всегда думал, что C устранено избыточность во имя эффективности. Является ли мое предположение неправильным или существует историческая причина этого? Если причина историческая, вы можете уточнить?

Ответ 1

История.

Harbison and Steel (5th Edition, 9.9 "Основная программа" ) говорит следующее:

Стандарт C требует, чтобы argv[argc] был нулевым указателем, но это не так в некоторых старых реализациях.

Ответ 2

Здесь история.

В первом выпуске UNIX, который предшествует C, exec взял в качестве аргументов имя файла и адрес списка указателей на NUL- завершенные строки аргументов, завершаемые указателем NULL. На странице man:

sys exec; name; args      / exec = 11.
name: <...\0>
...
args: arg1; arg2; ...; 0
arg1: <...\0>
...

Ядро подсчитывало аргументы и предоставляло новое изображение счетчику arg, за которым следует список указателей на копии строк аргументов в верхней части стека. На странице man:

sp--> nargs
      arg1
      ...
      argn

arg1: <arg1\0>
...
argn: <argn\0>

(Источник ядра здесь; я не смотрел, действительно ли ядро ​​написало что-то после указателя на последний аргумент. )

В какой-то момент, до 6-го издания, документация для exec, execl и execv начали замечать, что ядро ​​размещено a -1 после указателей arg. На странице руководства написано:

Argv напрямую не используется в другом execv, поскольку argv [argc] равен -1, а не 0.

В этот момент вы можете утверждать, что argc был избыточным, но программы в течение некоторого времени использовали его, а не просматривали список аргументов для -1. Например, здесь начало cal.c:

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

В 7-м выпуске exec было изменено, чтобы добавить указатель NULL после строк аргументов, и за ним последовал список указателей к строкам среды и другому NULL. На странице руководства написано:

Аргв напрямую можно использовать в другом execv, потому что argv [argc] равен 0.