1

In the C standard ISO/IEC 9899:2018 (C18), §5.1.2.2.1 - "Program startup", is written:

2 - If they are declared, the parameters to the main function shall obey the following constraints:

argv[argc] shall be a null pointer.


My Question is:

  • Why char *argv[argc]/char **argv shall be/ is a pointer to NULL?

This is what doesn´t make sense to me. argv[] is an array of pointers pointing to char. argv is a pointer to an array of pointers, pointing to char. Even if there are no flags given by the call, the pointer shouldn´t turn to a pointer to NULL.

Isn´t char* argv[] an array of pointers to char and char **argv a nested pointer to char pointers? How can a nested pointer to char (char**) be a pointer to NULL?


I have read the answers to argv[argc] ==? where the above-mentioned phrase from the standard is quoted as answers and also questioned to @pmg´s answer, who gave me the answer:

independent of the names used, by the c specification char *argv[argc] declares an array named argv capable of holding argc pointers to char. When passed to a function, the array gets converted to a pointer to its 1st element (so a pointer to pointer to char [this is why it is usual to see main(int argc, char **argv)]) losing information about size (char *a[10] has 10 elements; char **a is a pointer --- if the pointer points to an array, there is no way of knowing how many elements the underlying array has).

but unfortunately despite the humble effort, I still can´t understand why a pointer to pointer (nested pointer) to char (char**) turns into a pointer to NULL.

The answers to Is argv[argc] equal to NULL Pointer also do not answer why it shall be a pointer to NULL, only that is a pointer to NULL while quoting the above statement of the standard.


Thanks in advance.

  • 6
    Since `argc` is the number of elements in the `argv` array, the highest index of a valid entry is `argc-1`. That is, the last "argument" is `argv[argc-1]`. Technically, that could make `argv[argc]` undefined. The choice was made to make it NULL. What this does is provide additional ways of looping through the argument list (checking for NULL instead of using `argc` as a loop count). – lurker Jan 04 '20 at 16:49
  • 2
    The "why" needs references to 1970's data/authors - else we are left with speculation. – chux - Reinstate Monica Jan 04 '20 at 17:00
  • Probably that may be explained by historical reasons, but I don't know what those are. That constraint allows loops to traverse all command line arguments using only `argv`: `while (*argv) puts(*argv++);`. Perhaps that kind of loops were widespread at the time of standardization process. – Lxer Lx Jan 04 '20 at 17:33
  • *The "why" needs references to 1970's data/authors*. Are you sure that `argv[argc]` has been implemented as NULL going back to the 1970's? :) – lurker Jan 04 '20 at 17:51
  • 4
    `argv[argc]` is not a pointer to `NULL`. It is a null pointer. Its **value** is `NULL`. It does not point to `NULL`. – Eric Postpischil Jan 04 '20 at 18:25
  • @EricPostpischil what's the difference between a null pointer and a pointer to ```NULL```? – qwerty_url Jun 24 '21 at 20:10
  • 1
    @qwerty_url: `NULL` is (in effect) a value a pointer can have. (I say “in effect” because there are technical definitions of it and of “null pointer constant” that are not relevant here.) A pointer either points to some object or function or is a null pointer (in which case it does not point to any object or function). If we consider some `char *x` that has a defined value, then either `x` points to a `char` or it is a null pointer. If it is a null pointer, it does not point to anything; it does not point to `NULL`. – Eric Postpischil Jun 24 '21 at 20:34
  • 1
    @qwerty_url: If we had a `char **p`, that is a pointer to a pointer, so it could point to some place where we have stored a null pointer. Even so, this would be a pointer to a null pointer; it would not be a pointer to `NULL` (due to the technical definition of `NULL` as being a macro with a certain form). – Eric Postpischil Jun 24 '21 at 20:35

5 Answers5

4

It is done for two reasons:

  1. For extra safety: if you try accessing an element beyond [argc - 1] you will crash, instead of interpreting garbage as an extra argument, and doing something bad with it, like treating it as a filename and writing to the file.

  2. For convenience: this way, you can make use of argv without knowing argc.

How it works:

There is no magic really, here is the memory layout:

char**  |    char*     |  char[]
--------+--------------+---------
argv -> | argv[0] ->   | "arg1"
        | argv[1] ->   | "arg2"
        | argv[2] NULL |
Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • This does not answer the question asked. This answers why `argv[argc]` is `NULL`, but the question asks (incorrectly) why `argv[argc]` is a pointer to `NULL`. – Eric Postpischil Jan 04 '20 at 18:28
3

From

Rationale for International Standard Programming Languages C Revision 5.10 April-2003

5.1.2.2.1 Program startup

The specification of argc and argv as arguments to main recognizes extensive prior practice. argv[argc] is required to be a null pointer to provide a redundant check for the end of the list, also on the basis of common practice.


Update: As pointed out by Eric Postpischil, this answers why argv[argc] is a null pointer. Of course argv[argc] is not a pointer to NULL, as asked in the question (NULL is a macro defined as a null pointer constant; pointer to NULL makes no sense). So the question and answers may be rendered moot.

Lxer Lx
  • 292
  • 1
  • 6
2

What is meant by

— argv[argc] shall be a null pointer.

Is that index argc of the array argv is a null pointer.

That is

if (argv[argc] == NULL)

will always come out true.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
2

IMO it is for convenience while(*argv != NULL) ....

0___________
  • 60,014
  • 4
  • 34
  • 74
0

To answer the why.

The C standard did not invent this convention; it documented existing practice. In doing so, one of the goals was to let existing programs continue to work. Since there are existing programs that relied on argc, that parameter had to be conserved. And since there were other programs that relied on argv[] being NULL-terminated, that too had to be conserved.

The existing practice in turn was mostly an implementation detail of the early UNIX implementation.

MSalters
  • 173,980
  • 10
  • 155
  • 350