What You Will Learn
When you run a program from the shell, you can pass extra information to it on the command line. In C, these are available through argc (argument count) and argv (argument vector). Almost every Linux utility — ls, cp, grep — uses these.
int argc — the number of strings in argv (always at least 1).
char *argv[] — an array of pointers to null-terminated C strings.
argv[0] is always the name used to invoke the program.
argv[argc] is always a NULL pointer — marks the end of the list.
|
argc
3 |
→ |
argv (pointer array)
|
→ |
String data (above stack)
|
Since argv[0] holds the name used to invoke the program, you can create multiple hard links to one executable and have it behave differently based on its name. Classic example: gzip, gunzip, and zcat are all the same binary on disk, distinguished only by argv[0].
You can read the command-line arguments of any process (given its PID) from the kernel’s virtual filesystem:
$ cat /proc/self/cmdline | tr '\0' ' '
cat /proc/self/cmdline
$ cat /proc/1234/cmdline | tr '\0' ' '
/usr/bin/python3 myscript.py --verbose
# Each argument is separated by a null byte (\0)
# tr '\0' ' ' replaces nulls with spaces for display
Code Examples
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf("Number of arguments (argc): %d\n\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = \"%s\"\n", i, argv[i]);
}
/* argv[argc] is always NULL — safe sentinel */
printf("\nargv[%d] = %s (NULL terminator)\n",
argc, argv[argc] == NULL ? "NULL" : "not NULL");
return 0;
}
/* Compile: gcc -o myecho myecho.c
Run: ./myecho hello world 42
Output:
Number of arguments (argc): 4
argv[0] = "./myecho"
argv[1] = "hello"
argv[2] = "world"
argv[3] = "42"
argv[4] = NULL (NULL terminator) */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: %s num1 num2 ...\n", argv[0]);
return 1;
}
long sum = 0;
for (int i = 1; i < argc; i++) {
long val = strtol(argv[i], NULL, 10); /* safer than atoi */
sum += val;
printf(" Adding argv[%d] = %ld\n", i, val);
}
printf("Total sum = %ld\n", sum);
return 0;
}
/* Run: ./sum 10 20 30 40
Output:
Adding argv[1] = 10
Adding argv[2] = 20
Adding argv[3] = 30
Adding argv[4] = 40
Total sum = 100 */
#include <stdio.h>
#include <string.h>
/* One binary that behaves like two different tools
depending on the name it is invoked as. */
int main(int argc, char *argv[])
{
/* Extract just the basename (strip directory path) */
const char *prog = strrchr(argv[0], '/');
prog = (prog != NULL) ? prog + 1 : argv[0];
if (strcmp(prog, "say_hello") == 0) {
printf("Hello, %s!\n", argc > 1 ? argv[1] : "World");
} else if (strcmp(prog, "say_bye") == 0) {
printf("Goodbye, %s!\n", argc > 1 ? argv[1] : "World");
} else {
printf("Unknown invocation name: %s\n", prog);
}
return 0;
}
/* Setup and run:
$ gcc -o say_hello multi_name.c
$ ln say_hello say_bye (hard link)
$ ./say_hello Ravi
Hello, Ravi!
$ ./say_bye Ravi
Goodbye, Ravi!
Both run the SAME binary, different behaviour via argv[0]. */
Interview Preparation Questions
argc is the count of strings in the argv array. It is always at least 1 because argv[0] always contains the program’s invocation name. In practice argc is never 0 on a POSIX system.
argv[argc] is always a NULL pointer. This provides a second way to iterate through argv using a pointer loop: `for (char **p = argv; *p != NULL; p++)` — it stops when it hits the NULL sentinel.
The name used to invoke the program as typed on the command line — e.g., “./myprogram” or “/usr/bin/ls”. This may include a path prefix. Programs like gzip use this to serve multiple functions under different link names.
Controlled by ARG_MAX (at least 4096 bytes per POSIX). On Linux kernel 2.6.23+, the limit is 1/4 of the soft RLIMIT_STACK in effect at exec time — typically around 2 MB. Query with: sysconf(_SC_ARG_MAX).
The argv strings (and environ strings) reside in a single contiguous region just above the initial stack in the process’s virtual memory — above the stack frames for main() and C runtime startup. They are placed there by the kernel before the process starts.
atoi() has no error detection — if argv[i] is not a valid number it silently returns 0. strtol() lets you pass an endpointer to detect non-numeric characters and sets errno on overflow, making it the safe, portable choice for production code.
