- Option and Argument Conventions
- Basic Command-Line Processing
- Option Parsing: getopt() and getopt_long()
- The Environment
- Summary
- Exercises
2.2 Basic Command-Line Processing
A C program accesses its command-line arguments through its parameters, argc and argv. The argc parameter is an integer, indicating the number of arguments there are, including the command name. There are two common ways to declare main(), varying in how argv is declared:
int main(int argc, char *argv[]) int main(int argc, char **argv) { { ... ... } }
Practically speaking, there's no difference between the two declarations, although the first is conceptually clearer: argv is an array of pointers to characters. The second is more commonly used: argv is a pointer to a pointer. Also, the second definition is technically more correct, and it is what we use. Figure 2.2 depicts this situation.
Figure 2.2. Memory for argv
By convention, argv[0] is the program's name. (For details, see Section 9.1.4.3, "Program Names and argv[0]," page 297.) Subsequent entries are the command line arguments. The final entry in the argv array is a NULL pointer.
argc indicates how many arguments there are; since C is zero-based, it is always true that 'argv[argc] == NULL'. Because of this, particularly in Unix code, you will see different ways of checking for the end of arguments, such as looping until a counter is greater than or equal to argc, or until 'argv[i] == 0' or while '*argv != NULL' and so on. These are all equivalent.
2.2.1 The V7 echo Program
Perhaps the simplest example of command-line processing is the V7 echo program, which prints its arguments to standard output, separated by spaces and terminated with a newline. If the first argument is -n, then the trailing newline is omitted. (This is used for prompting from shell scripts.) Here's the code:1
1 #include <stdio.h> 2 3 main(argc, argv) int main(int argc, char **argv) 4 int argc; 5 char *argv[]; 6 { 7 register int i, nflg; 8 9 nflg = 0; 10 if(argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n') { 11 nflg++; 12 argc; 13 argv++; 14 } 15 for(i=1; i<argc; i++) { 16 fputs(argv[i], stdout); 17 if (i < argc-1) 18 putchar(' '); 19 } 20 if(nflg == 0) 21 putchar('\n'); 22 exit(0); 23 }
Only 23 lines! There are two points of interest. First, decrementing argc and simultaneously incrementing argv (lines 12 and 13) are common ways of skipping initial arguments. Second, the check for -n (line 10) is simplistic. -no-newline-at-the-end also works. (Compile it and try it!)
Manual option parsing is common in V7 code because the getopt() function hadn't been invented yet.
Finally, here and in other places throughout the book, we see use of the register keyword. At one time, this keyword provided a hint to the compiler that the given variables should be placed in CPU registers, if possible. Use of this keyword is obsolete; modern compilers all base register assignment on analysis of the source code, ignoring the register keyword. We've chosen to leave code using it alone, but you should be aware that it has no real use anymore.2