Command Line Arguments

C programs can receive parameters from the command line.

For example, running:

1
$ ./foo hello world

The program foo receives two command line arguments: hello and world.

How does the program access these command line arguments? C stores the command line input in an array, which can be accessed through the parameters of the main() function.

1
2
3
4
5
6
7
#include <stdio.h>

int main(int argc, char* argv[]) {
for (int i = 0; i < argc; i++) {
printf("arg %d: %s\n", i, argv[i]);
}
}

In this example, the main() function has two parameters: argc (argument count) and argv (argument variable). While these parameter names can be anything, the convention is to use these specific terms.

The first parameter, argc, represents the number of command line arguments. Since the program name is included, argc is technically the number of arguments plus one.

The second parameter, argv, is an array that holds all the command line inputs, with each element being a string pointer.

For the command ./foo hello world, argc is 3, indicating three components: ./foo, hello, and world. The argv array holds these inputs: argv[0] is the program name ./foo, argv[1] is hello, and argv[2] is world. Generally, argv[1] to argv[argc - 1] represent all command line parameters, and argv[argc] is a NULL pointer.

String pointers can be viewed as character arrays, so the following two declarations are equivalent:

1
2
3
4
5
// Option 1
int main(int argc, char* argv[])

// Option 2
int main(int argc, char** argv)

Moreover, each command line argument can be accessed either through the array notation argv[i] or pointer notation *(argv + i).

Using argc, you can enforce the number of parameters a function accepts:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main(int argc, char** argv) {
if (argc != 3) {
printf("usage: mult x y\n");
return 1;
}

printf("%d\n", atoi(argv[1]) * atoi(argv[2]));
return 0;
}

In this example, if argc is not equal to 3, an error is returned, ensuring that the program requires two parameters to run.

Additionally, the last element of the argv array is a NULL pointer (argv[argc] == NULL), allowing for traversal in the following manner:

1
2
3
for (char** p = argv; *p != NULL; p++) {
printf("arg: %s\n", *p);
}

Here, the pointer p moves through each member of argv, terminating when it reaches the NULL pointer. Since the address of argv is fixed, it cannot be incremented directly; thus, an intermediate variable p is used for the traversal.

Exit Status

C specifies that if the main() function lacks a return statement, it implicitly adds return 0, indicating successful termination. This is why it’s customary for main() to return an integer value, with a return value of 0 signifying success and any non-zero value indicating an error.

You can check the exit status of the last command using the Bash environment variable $?:

1
2
3
$ ./foo hello world
$ echo $?
0

In this case, echo $? prints the value of $?, which is 0, indicating the previous command executed successfully.

Note that only main() has this default behavior; other functions do not.

Environment Variables

C provides the getenv() function (declared in stdlib.h) for reading command line environment variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>

int main(void) {
char* val = getenv("HOME");

if (val == NULL) {
printf("Cannot find the HOME environment variable\n");
return 1;
}

printf("Value: %s\n", val);
return 0;
}

In this example, getenv("HOME") retrieves the $HOME environment variable. If this variable is NULL, the program returns an error.