C Command-Line Arguments: argc, argv, Parsing & Validation Masterclass

โšก C (C17 / C23 Standard) ๐ŸŸข Lesson 42 ๐Ÿ“‚ Phase 16: Command-Line Arguments ๐Ÿ“… 2026 Comprehensive Master Edition
๐Ÿ“Œ Covered in this in-depth guide: argc & argv[] ยท argv[0] Program Name ยท String-to-Number (atoi, strtol, strtod) ยท Argument Validation ยท getopt() Introduction ยท Building CLI tools

Welcome to Phase 16 (Chapter 42): C Command-Line Arguments โ€” argc, argv, Parsing & Validation Masterclass! Every C program can receive arguments from the operating system shell at launch time. Mastering argc and argv transforms your programs from hardcoded toys into flexible, reusable command-line tools.

1argc & argv Architecture
Shell invocation: $ ./calculator 10 add 20 argc = 4 (Total number of arguments, including the program name) argv (char*[]): argv[0] โ”€โ”€โ–บ "./calculator" (Always the program name/path) argv[1] โ”€โ”€โ–บ "10" (First user-provided argument) argv[2] โ”€โ”€โ–บ "add" (Second argument) argv[3] โ”€โ”€โ–บ "20" (Third argument) argv[4] โ”€โ”€โ–บ NULL (Always NULL-terminated sentinel!)

ALL arguments arrive as strings (char*). To work with them as numbers you must convert explicitly using atoi(), strtol(), or strtod().

atoi() vs strtol() โ€” Which to Use?

โ€ข atoi(str) โ€” Simple but UNSAFE. Returns 0 for invalid input ("abc") indistinguishable from the number 0. No overflow detection.

โ€ข strtol(str, &endptr, base) โ€” SAFE. Sets endptr to first invalid character. Detects overflow. Always prefer this for production code.

2Complete CLI Calculator Program
C โ€” Robust Command-Line Calculatorโ–ถ Try in Compiler
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static long safe_parse_long(const char *str, const char *name) {
    char *end;
    errno = 0;
    long val = strtol(str, &end, 10);
    if (errno != 0 || *end != '\0' || end == str) {
        fprintf(stderr, "Error: '%s' is not a valid integer for %s\n", str, name);
        exit(EXIT_FAILURE);
    }
    return val;
}

int main(int argc, char *argv[]) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s <num1> <op: add|sub|mul|div> <num2>\n", argv[0]);
        return EXIT_FAILURE;
    }

    long a = safe_parse_long(argv[1], "num1");
    long b = safe_parse_long(argv[3], "num2");
    const char *op = argv[2];

    if      (strcmp(op, "add") == 0) printf("%ld + %ld = %ld\n", a, b, a + b);
    else if (strcmp(op, "sub") == 0) printf("%ld - %ld = %ld\n", a, b, a - b);
    else if (strcmp(op, "mul") == 0) printf("%ld * %ld = %ld\n", a, b, a * b);
    else if (strcmp(op, "div") == 0) {
        if (b == 0) { fprintf(stderr, "Error: Division by zero\n"); return 1; }
        printf("%ld / %ld = %ld\n", a, b, a / b);
    } else {
        fprintf(stderr, "Error: Unknown operator '%s'\n", op);
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
3Technical FAQs

Q1: What is argv[argc]?

By C standard guarantee, argv[argc] is always NULL. This allows null-terminated traversal: for (char **p = argv; *p; p++).

Q2: Can argv strings be modified?

Yes! Unlike string literals, argv strings are writable. You can modify argv[1][0] = 'X'; safely. But do not replace the pointer itself.

Q3: What is the difference between strtol() and sscanf() for parsing?

strtol() is more precise for integer parsing with overflow detection. sscanf() is convenient for mixed-format parsing but provides less error granularity.

Q4: How do I read optional flags like -v or --verbose?

Use POSIX getopt(argc, argv, "vho:") from <unistd.h> for Unix systems, or write a manual loop checking argv[i][0] == '-'.

Q5: How do environment variables differ from command-line arguments?

Command-line args are positional and explicit per invocation. Environment variables are inherited from the shell session and accessible via getenv("PATH").