28

A comment on this answer to a StackOverflow question made me curious. According the C99 standard, section 5.1.2.2.1:

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

int main(void) { /*...*/ }

or with two parameters (referred to here as argc and argv,though any names may be used, as they are local to the function in which they are declared):

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

or equivalent; or in some other implementation-defined manner.

This was the case in ANSI C as well, it seems; I'm unaware of any earlier standards besides K&R.

The second signature, with two parameters, has always included argc, the number of arguments in argv. Given that argv is a NULL-terminated list, however, it's trivial to calculate this number.

Historically, is there a reason for including argc as a parameter of main? Was it a feature of earlier languages such as B? Was it to save computation (at the cost of potentially using a bit more memory)? Was it just a programmer convenience?

  • A common extension is a third parameter, char *envp[] (or equivalently char **envp). There is no count parameter associated with it, it's just a pointer to the first element of a NULL-terminated array of pointers. – Keith Thompson Dec 15 '17 at 19:54
41

K&R first edition (1978) does not mention the NULL terminator in the argv list at all. This was added later, in ANSI C. The relevant paragraph (section 5.11, p. 110) is:

In environments that support C, there is a way to pass command-line arguments or parameters to a program when it begins executing. When main is called to begin execution, it is called with two arguments. The first (conventionally called argc) is the number of command-line arguments the program was invoked with; the second (argv) is a pointer to an array of character strings that contain the arguments, one per string. Manipulating these character strings is a common use of multiple levels of pointers.

Of course, when the terminating NULL was added to the standard, there was no possibility of changing the number of arguments passed to main.

27

The requirement that argv[argc] be null was added in the first version of the ANSI C standard; it wasn’t a requirement before then, and some environments didn’t null-terminate the array (the strings themselves were of course null-terminated). In fact, in Unix V4, argv[argc] was documented as being -1 and not 0. Thus before ANSI C, argc was necessary, and it was kept thereafter for backwards compatibility.

  • 8
    @tonysdg Even today the fact that the argv array is NULL terminated isn't well known. It wouldn't surprise me if there were a number of pre-ANSI non-Unix C compilers that didn't do this. A different but related issue is that a lot of programs assume argc can never be 0 despite this not being guaranteed. – Ross Ridge Dec 13 '17 at 20:04
  • 4
    @Wumpus right, it’s a mixture of calling convention from the kernel, C library and compiler — main isn’t the real entry point into a program, there’s C library code that’s executed first and can massage the arguments to main. – Stephen Kitt Dec 13 '17 at 20:42
  • 2
    @StephenKitt I may be wrong, but I think main was the real entry point back then. The stuff that happens before main nowadays is all stuff that didn't exist until later. – Wumpus Q. Wumbley Dec 13 '17 at 20:43
  • 3
    @WumpusQ.Wumbley Nop. The real entry point of a Unix programm was always the first word. This is where the linker puts the C-lib, which in turn, after seting up various stuff (like file I/O tabels or parsing aguments), thenn calls main() with argc,argv. – Raffzahn Dec 13 '17 at 20:47
  • 3
    @Wilson On Unix systems you just need to do execl("/path/to/program", NULL). (POSIX says "The argument arg0 should point to a filename string that is associated with the process being started", emphasis mine, so NULL is allowed here but discouraged.) More generally it's easy to imagine an implementation of C on some platform (eg. embedded) without any concept of command line arguments and so argc is always 0. – Ross Ridge Dec 13 '17 at 21:33
11

Originally argv[argc] did not exist in Unix / K&R C.

Adding a NULL pointer is a later added feature against 'lazy' programming. This still doesn't make it a 'NULL-terminated' List, as there is no guarantee that the list doesn't already contain a NULL pointer due whatever previous processing.


argv is by definition an array (vector in BCPL notation) of string pointers with argc elements thus only indices of 0..[argc-1] are valid. K&R's original The C Programming Language from 1978 includes only examples (chapter 5.11) where argc is used to determinate boundries.

The same is true for all examples in 1988's second edition of The C Programming Language. No usage of argv[argc]. But now a new half sentence is added on page 115: '...additionally, the standard requires that argv[argc] be a null pointer.' followed by an ilustration of an example vector. The 'standard' referenced here is the proposed ANSI standard which was basicly finished, but not published until 1989 (C89).

Before that, the X/Open group (holder of the Unix trademark back then) tried to define what Unix or better a unixoid environment should be by creating several standard books called XPG (X/Open Portability Guide). XPG2 from 1987 (?) defines among others the C programming language interface including the C-lib which in turn prepares argc/v when calling main(). Here a new, last NULL element for main's argv was included.

The X/Open definitions are the most important source that went into C89. So it may be safe to see this as a point where it 'officially' entered C. Now which of the Unix(oide) systems did bring it into XPG2 is not fully known to me. AFAIK it is based on an implementation for some BULL system of the early 80s.


P.S.: And while it may be 'easy' to calculate the number of arguments by runing along the list until the first NULL is encountered, this is neither secure, nor free, but costs processing time. So why should a (user) program spend many cycles to calculate that number, when the calling program (OS/C-Lib) has already done the job while preparing the list? Remember, once upon a time programming was sensible and about performance :))

  • The idea of null-terminated lists of pointers pre-dates K&R C since Unix V4’s exec already uses them. (I’m not saying you’re saying it doesn’t, just commenting.) – Stephen Kitt Dec 13 '17 at 20:48
  • @StephenKitt Erm, I didn't say Null-terminated lists in generl are some new inventions. it's only about how argv and C – Raffzahn Dec 13 '17 at 20:49
  • 1
    Sorry, I misinterpreted “Adding a NULL pointer is a later added feature against 'lazy' programming.” as a general comment, rather than specific to argv! – Stephen Kitt Dec 13 '17 at 22:12
  • 10
    "argv is by definition an array (vector in BCPL notation)" Now we know why it's argv instead of arga... – RonJohn Dec 14 '17 at 1:00
3

Looking back in the context of recent ideas in programming languages, we can now reason it in more than one ways.

(1) Precondition Validation and ease of use: Even if it's a NULL terminated list, just to know how many arguments are passed in, one has to go through the entire list to count the number, and every program that uses command line parameters needs to do that to work correctly. Before proceeding with checking the validity of the arguments passed in, and then with their eventual use, the program needs a precondition that terminates the program perhaps with a 'usage help' statement if the number of arguments sent in does not match with the number of arguments expected. So a 'ready-made' argc is quite handy.

(2) Consistent Style: Secondly, arrays are raw in C, and the program cannot find out the size of an array given the array pointer variable unless some sort of termination is encoded as a special value held inside the array. Such a special value may not always be possible if one wants to use the entire range of the type of the values being held in the array. The use of argc is consistent with this primitive choice.

(3) Arrays are not self-aware: Languages that treat arrays as objects that know their own cardinalities do not need argc. So, only they could drop the parameter altogether. This is not the case with C.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.