Command-Line Arguments

Most real programs take their input from the command line: gcc main.c -o main, git commit -m "…". In C, main can receive those words through two parameters, argc and argv.

argc and argv

C
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = \"%s\"\n", i, argv[i]);
    }
    return 0;
}
Output
argc = 4
argv[0] = "./prog"
argv[1] = "report.csv"
argv[2] = "--verbose"
argv[3] = "42"

This was run as ./prog report.csv --verbose 42:

  • argc is the number of arguments, including the program's own name.
  • argv is an array of strings; argv[0] is the program name (or path), and argv[argc] is NULL.
  • Every argument is a string, even 42 — convert it yourself.

The shell splits arguments at spaces, so to pass text containing spaces, quote it: ./prog "hello world".

Converting and checking

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

int parse_int(const char *text, long *out) {
    char *end;
    errno = 0;
    long value = strtol(text, &end, 10);
    if (end == text || *end != '\0' || errno == ERANGE) return 0;
    *out = value;
    return 1;
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "usage: %s WIDTH HEIGHT\n", argv[0]);
        return 1;
    }
    long w, h;
    if (!parse_int(argv[1], &w) || !parse_int(argv[2], &h)) {
        fprintf(stderr, "WIDTH and HEIGHT must be whole numbers\n");
        return 1;
    }
    printf("area: %ld\n", w * h);
    return 0;
}
Output
area: 360

Run as ./prog 12 30. A program should check how many arguments it got and print a usage message to stderr — and exit with a non-zero status — when they're wrong. Here it was run as ./prog 12:

C
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "usage: %s WIDTH HEIGHT\n", argv[0]);
        return 1;
    }
    return 0;
}
Output
usage: ./prog WIDTH HEIGHT

Options

Unix programs conventionally take options starting with -. A simple loop handles them:

C
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

int main(int argc, char *argv[]) {
    int repeat = 1, upper = 0;
    int i = 1;
    for (; i < argc && argv[i][0] == '-'; i++) {
        if (strcmp(argv[i], "-u") == 0) {
            upper = 1;
        } else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
            repeat = atoi(argv[++i]);
        } else {
            fprintf(stderr, "unknown option %s\n", argv[i]);
            return 1;
        }
    }
    for (int r = 0; r < repeat; r++) {
        for (int j = i; j < argc; j++) {
            for (const char *p = argv[j]; *p; p++) {
                putchar(upper ? toupper((unsigned char) *p) : *p);
            }
            putchar(j + 1 < argc ? ' ' : '\n');
        }
    }
    return 0;
}
Output
HELLO WORLD
HELLO WORLD
HELLO WORLD

This was run as ./prog -n 3 -u hello world. For anything bigger, use a library: POSIX systems provide getopt (and GNU adds getopt_long for --long options).

Exit status

The value main returns is the program's exit status. Scripts and build tools check it: 0 means success, anything else failure. <stdlib.h> defines EXIT_SUCCESS and EXIT_FAILURE, and exit(status) ends the program from anywhere (lesson 50).

Environment variables

getenv reads settings from the environment — another common way to configure a program:

C
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    const char *level = getenv("LOG_LEVEL");
    if (level == NULL) level = "info";      // default when not set
    printf("logging at %s level\n", level);
    return 0;
}
Output
logging at info level

Run as LOG_LEVEL=debug ./prog to change it.

Try it yourself

Write a tiny sum command that adds up all its numeric arguments and reports any that aren't numbers.

Try it yourself · main.c
Edit the code, copy it into main.c, then compile and run it with cc -std=c17 -Wall main.c -o main && ./main. How to set up a C compiler
Output
skipping "x"
sum = 22.5