Command-Line Arguments in C: argc and argv Explained

Pointer array diagram showing int argc = 4 and char *argv[] slots pointing to null-terminated CLI argument strings terminated by NULL.
Pointer array diagram showing int argc = 4 and char *argv[] slots pointing to null-terminated CLI argument strings terminated by NULL.

In a hosted C program, main can receive an argument count and an array of pointers to strings: int main(int argc, char *argv[]). Check the count before using an argument, distinguish the final NULL pointer from a string terminator, and validate numeric text instead of assuming atoi can report errors. The example supports real command-line input and an explicitly labeled no-argument demo.

### The argument vector and its two kinds of terminator

For a typical invocation ./app -n 10 input.txt, argc is 4. argv[0] points to the program name, argv[1] to -n, argv[2] to 10 and argv[3] to input.txt. Each argument is a null-terminated character string. argv[argc] is a null pointer that ends the pointer array; it is not an extra empty string. Never print it with %s or dereference it. The char *argv[] parameter is adjusted to char **argv, so sizeof argv does not count arguments.

### What the C standard does and does not promise

argc is nonnegative. If argc is greater than zero, argv[0] represents the program name; its first character may be null if the name is unavailable. Do not assume it is an absolute executable path or dereference argv[0] when argc is zero. Hosted implementations support the standard two-parameter main form; embedded freestanding startup can differ. The program must validate the shape of its inputs before accessing argv[1] or argv[2].

### Shell quoting is outside C

On a usual shell, ./app -n 10 "input file.txt" supplies one filename containing a space. The shell interprets quoting and expansion before launching the program; C receives the resulting strings, not the shell's original command text. Other launchers and operating systems have their own argument construction rules. Do not implement a pretend shell by splitting stdin on spaces and calling the result real argv.

### A small validated command grammar

This program accepts exactly -n COUNT FILE, where COUNT is a base-10 integer from 0 through 1000000. It checks argc and the flag before accessing operands. strtol receives an end pointer; errno is reset first, ERANGE is rejected, and trailing text is rejected. An empty string is invalid. This is an argument-parsing example only: the filename is displayed, not opened. A production utility should define additional policies for empty paths, repeated flags and filenames beginning with a hyphen.

### Running locally and in a browser compiler

Build with gcc -std=c17 -Wall -Wextra -Werror main.c -o app, then run ./app -n 10 input.txt. Standard input is a separate channel and does not set argc or argv. The browser runner does not promise a command-line argument control, so when no user arguments arrive, main passes a clearly labeled built-in vector to the same parser. The expected output below is for that demo, not a claim that the site injected process arguments. Supplying actual arguments uses the real vector instead.

### Diagnostics to try

A missing operand, a wrong flag, 10x, a negative count or an out-of-range number yields a message on stderr and EXIT_FAILURE. Compile with warnings enabled to catch pointer/type mistakes. Check bounds before using a sanitizer: even a null-pointer sentinel at argv[argc] is not a valid string argument. This parser uses only standard C library calls; getopt is a useful option parser on supported systems but is not part of ISO C.

Interactive Runnable C Implementation

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static int show_arguments(int argc, char *argv[]) {
    if (argc != 4 || strcmp(argv[1], "-n") != 0) {
        fputs("Usage: app -n COUNT FILE\n", stderr);
        return EXIT_FAILURE;
    }
    errno = 0;
    char *end;
    long count = strtol(argv[2], &end, 10);
    if (end == argv[2] || *end != '\0' || errno == ERANGE ||
        count < 0 || count > 1000000) {
        fputs("COUNT must be an integer from 0 to 1000000\n", stderr);
        return EXIT_FAILURE;
    }
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; ++i)
        printf("argv[%d] = %s\n", i, argv[i]);
    printf("argv[argc] is NULL: %s\n", argv[argc] == NULL ? "yes" : "no");
    printf("Count: %ld; file: %s\n", count, argv[3]);
    return EXIT_SUCCESS;
}

int main(int argc, char *argv[]) {
    if (argc > 1) return show_arguments(argc, argv);
    char program[] = "./app", flag[] = "-n";
    char count[] = "10", file[] = "input.txt";
    char *demo[] = {program, flag, count, file, NULL};
    puts("Mode: built-in argv demo");
    return show_arguments(4, demo);
}

Open example in compiler

Expected Output

Mode: built-in argv demo
argc = 4
argv[0] = ./app
argv[1] = -n
argv[2] = 10
argv[3] = input.txt
argv[argc] is NULL: yes
Count: 10; file: input.txt

Frequently Asked Questions

Does argc include the program name?

In a typical hosted invocation, yes: argv[0] is the program name and is counted. Portable code must still allow the standard's argc equal to zero case before accessing argv[0].

Are char **argv and char *argv[] equivalent in main?

Yes, as function parameter declarations. The array parameter is adjusted to a pointer; this does not turn sizeof argv into an argument count.

Why does typing into stdin not change argv?

Arguments are supplied when the process is launched. stdin is a stream read during execution by functions such as fgets or scanf.

Why is atoi not enough for validating a count?

It does not provide an end pointer or reliable error reporting to distinguish invalid input from a valid zero. Use strtol and explicitly check conversion, range and trailing characters.

Related examples