6.6 Command-Line Arguments (argc, argv)

6.6 Command-Line Arguments (argc, argv)
Linux System Programming — Chapter 6
2
Key Variables
3
Code Examples
6
Interview Questions

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.

Understanding argc and argv

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.

Memory Layout for: ./myecho hello world
argc
3
argv (pointer array)
argv[0] →
argv[1] →
argv[2] →
argv[3] = NULL
String data (above stack)
./myecho\0
hello\0
world\0

The argv[0] Program Name Trick

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].

Accessing Command-Line Args from /proc

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

Example 1: Print All Command-Line Arguments
#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) */
Example 2: Summing Numbers Passed as Arguments
#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 */
Example 3: Behave Differently Based on argv[0]
#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

Q1. What does argc represent? Can it ever be 0?

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.

Q2. What is argv[argc]?

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.

Q3. What is stored in argv[0]?

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.

Q4. What is the upper limit on the total size of argv and environment strings?

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).

Q5. Where in memory do argv strings live?

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.

Q6. Why use strtol() instead of atoi() to parse argv numbers?

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.

Leave a Reply

Your email address will not be published. Required fields are marked *