Docs

README

Command Line Arguments in C

Table of Contents

  1. •Introduction
  2. •Understanding argc and argv
  3. •How Command Line Arguments Work
  4. •Accessing Command Line Arguments
  5. •Converting String Arguments
  6. •Argument Parsing Techniques
  7. •Using getopt for Option Parsing
  8. •Using getopt_long for Long Options
  9. •Building Command Line Utilities
  10. •Environment Variables
  11. •Best Practices
  12. •Common Use Cases
  13. •Error Handling
  14. •Summary

Introduction

Command line arguments are parameters passed to a program when it is executed from the command line or terminal. They provide a way for users to customize the behavior of a program without modifying its source code or using interactive input.

Why Use Command Line Arguments?

  1. •Flexibility: Allow users to control program behavior at runtime
  2. •Automation: Enable programs to be used in scripts and batch processing
  3. •Non-Interactive Operation: Programs can run without user intervention
  4. •Configuration: Pass settings and options without configuration files
  5. •UNIX Philosophy: Enables piping and chaining of commands

Common Examples

# Copying a file
cp source.txt destination.txt

# Compiling C code with options
gcc -Wall -o program program.c

# Listing files with options
ls -la /home/user

# Searching for patterns
grep -i "pattern" file.txt

Understanding argc and argv

In C, command line arguments are passed to the main() function through two special parameters:

argc (Argument Count)

  • •Type: int
  • •Purpose: Stores the total number of command line arguments
  • •Value: Always at least 1 (the program name itself counts as the first argument)
  • •Usage: Used to determine how many arguments were passed

argv (Argument Vector)

  • •Type: char *argv[] or char **argv (array of character pointers)
  • •Purpose: Stores the actual argument strings
  • •Organization:
    • •argv[0] - Program name (or path)
    • •argv[1] - First argument
    • •argv[2] - Second argument
    • •argv[argc] - Always NULL (null pointer terminator)

Main Function Signatures

// Standard signature with command line arguments
int main(int argc, char *argv[])

// Alternative pointer notation
int main(int argc, char **argv)

// With environment variables
int main(int argc, char *argv[], char *envp[])

// Minimal (without command line arguments)
int main(void)

Memory Layout

argv (char **)
    |
    v
+--------+--------+--------+--------+--------+------+
| argv[0]| argv[1]| argv[2]| argv[3]| argv[4]| NULL |
+--------+--------+--------+--------+--------+------+
    |        |        |        |        |
    v        v        v        v        v
"program" "-f"   "file.txt" "-v"    "100"

How Command Line Arguments Work

Execution Flow

  1. •User Types Command: User enters a command in the terminal
  2. •Shell Parses Input: The shell splits the command by whitespace
  3. •OS Creates Process: Operating system creates a new process
  4. •Arguments Passed: OS passes arguments to the program's main function
  5. •Program Accesses: Program accesses arguments through argc and argv

Argument Parsing by Shell

# Command typed by user
./program -f file.txt "hello world" 123

# Shell creates these arguments:
# argv[0] = "./program"
# argv[1] = "-f"
# argv[2] = "file.txt"
# argv[3] = "hello world"  (quotes removed, space preserved)
# argv[4] = "123"
# argc = 5

Special Shell Characters

# Quotes preserve spaces
./program "hello world"     # argv[1] = "hello world"

# Escape characters
./program hello\ world      # argv[1] = "hello world"

# Single quotes preserve literally
./program '$HOME'           # argv[1] = "$HOME"

# Double quotes allow variable expansion
./program "$HOME"           # argv[1] = "/home/user"

Accessing Command Line Arguments

Basic Access

#include <stdio.h>

int main(int argc, char *argv[]) {
    // Print number of arguments
    printf("Number of arguments: %d\n", argc);

    // Access program name
    printf("Program name: %s\n", argv[0]);

    // Access individual arguments
    if (argc > 1) {
        printf("First argument: %s\n", argv[1]);
    }

    // Access all arguments using index
    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }

    return 0;
}

Using Pointer Arithmetic

#include <stdio.h>

int main(int argc, char **argv) {
    // Using pointer arithmetic
    char **ptr = argv;

    while (*ptr != NULL) {
        printf("%s\n", *ptr);
        ptr++;
    }

    return 0;
}

Checking for Null Terminator

#include <stdio.h>

int main(int argc, char *argv[]) {
    // argv[argc] is always NULL
    int i = 0;
    while (argv[i] != NULL) {
        printf("Argument %d: %s\n", i, argv[i]);
        i++;
    }

    // Verify NULL terminator
    printf("argv[%d] is %s\n", argc,
           argv[argc] == NULL ? "NULL" : "NOT NULL");

    return 0;
}

Converting String Arguments

All command line arguments are passed as strings. To use them as other data types, conversion is necessary.

Converting to Integers

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

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s <number>\n", argv[0]);
        return 1;
    }

    // Method 1: atoi (simple but no error checking)
    int num1 = atoi(argv[1]);
    printf("atoi: %d\n", num1);

    // Method 2: strtol (recommended - with error checking)
    char *endptr;
    long num2 = strtol(argv[1], &endptr, 10);

    if (*endptr != '\0') {
        printf("Invalid number: %s\n", argv[1]);
        return 1;
    }
    printf("strtol: %ld\n", num2);

    return 0;
}

Converting to Floating Point

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

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s <float>\n", argv[0]);
        return 1;
    }

    // Method 1: atof (simple)
    double num1 = atof(argv[1]);
    printf("atof: %f\n", num1);

    // Method 2: strtod (with error checking)
    char *endptr;
    errno = 0;
    double num2 = strtod(argv[1], &endptr);

    if (errno == ERANGE) {
        printf("Number out of range\n");
        return 1;
    }
    if (*endptr != '\0') {
        printf("Invalid number format\n");
        return 1;
    }
    printf("strtod: %f\n", num2);

    return 0;
}

Conversion Functions Summary

FunctionConverts ToError HandlingHeader
atoi()intNonestdlib.h
atol()longNonestdlib.h
atof()doubleNonestdlib.h
strtol()longYes (endptr, errno)stdlib.h
strtoll()long longYes (endptr, errno)stdlib.h
strtoul()unsigned longYes (endptr, errno)stdlib.h
strtod()doubleYes (endptr, errno)stdlib.h
strtof()floatYes (endptr, errno)stdlib.h

Argument Parsing Techniques

Manual Parsing

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

void print_usage(const char *program_name) {
    printf("Usage: %s [options] <filename>\n", program_name);
    printf("Options:\n");
    printf("  -h, --help     Show this help message\n");
    printf("  -v, --verbose  Enable verbose mode\n");
    printf("  -n <number>    Specify a number\n");
}

int main(int argc, char *argv[]) {
    int verbose = 0;
    int number = 0;
    char *filename = NULL;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            return 0;
        }
        else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
            verbose = 1;
        }
        else if (strcmp(argv[i], "-n") == 0) {
            if (i + 1 >= argc) {
                fprintf(stderr, "Error: -n requires a number\n");
                return 1;
            }
            number = atoi(argv[++i]);
        }
        else if (argv[i][0] == '-') {
            fprintf(stderr, "Unknown option: %s\n", argv[i]);
            return 1;
        }
        else {
            filename = argv[i];
        }
    }

    if (filename == NULL) {
        fprintf(stderr, "Error: No filename specified\n");
        print_usage(argv[0]);
        return 1;
    }

    printf("Verbose: %s\n", verbose ? "yes" : "no");
    printf("Number: %d\n", number);
    printf("Filename: %s\n", filename);

    return 0;
}

Parsing Flags and Values

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

typedef struct {
    int help;
    int version;
    int count;
    char *input_file;
    char *output_file;
} Options;

int parse_arguments(int argc, char *argv[], Options *opts) {
    // Initialize defaults
    opts->help = 0;
    opts->version = 0;
    opts->count = 1;
    opts->input_file = NULL;
    opts->output_file = NULL;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            opts->help = 1;
        }
        else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
            opts->version = 1;
        }
        else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--count") == 0) {
            if (++i >= argc) return -1;
            opts->count = atoi(argv[i]);
        }
        else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--input") == 0) {
            if (++i >= argc) return -1;
            opts->input_file = argv[i];
        }
        else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) {
            if (++i >= argc) return -1;
            opts->output_file = argv[i];
        }
        else if (argv[i][0] == '-') {
            fprintf(stderr, "Unknown option: %s\n", argv[i]);
            return -1;
        }
        else {
            // Positional argument
            if (opts->input_file == NULL) {
                opts->input_file = argv[i];
            } else if (opts->output_file == NULL) {
                opts->output_file = argv[i];
            }
        }
    }

    return 0;
}

Using getopt for Option Parsing

The getopt() function provides a standard way to parse command line options on UNIX-like systems.

Basic getopt Usage

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>  // For getopt

int main(int argc, char *argv[]) {
    int opt;
    int verbose = 0;
    int debug = 0;
    char *filename = NULL;
    int count = 1;

    // Option string: "vdf:c:"
    // v, d - flags (no argument)
    // f:   - requires argument
    // c:   - requires argument

    while ((opt = getopt(argc, argv, "vdf:c:")) != -1) {
        switch (opt) {
            case 'v':
                verbose = 1;
                break;
            case 'd':
                debug = 1;
                break;
            case 'f':
                filename = optarg;  // optarg contains the argument
                break;
            case 'c':
                count = atoi(optarg);
                break;
            case '?':
                // Unknown option or missing argument
                fprintf(stderr, "Usage: %s [-v] [-d] [-f file] [-c count]\n",
                        argv[0]);
                return 1;
        }
    }

    printf("Verbose: %d\n", verbose);
    printf("Debug: %d\n", debug);
    printf("Filename: %s\n", filename ? filename : "(none)");
    printf("Count: %d\n", count);

    // Process remaining arguments (non-option arguments)
    // optind is the index of the first non-option argument
    for (int i = optind; i < argc; i++) {
        printf("Non-option argument: %s\n", argv[i]);
    }

    return 0;
}

getopt Option String Syntax

CharacterMeaning
aOption '-a' with no argument
a:Option '-a' requires an argument
a::Option '-a' has optional argument (GNU extension)
: at startReturn ':' for missing argument instead of '?'

getopt Global Variables

VariableDescription
optargPointer to the argument of the current option
optindIndex of the next element to be processed in argv
opterrIf non-zero, getopt prints error messages (default: 1)
optoptContains the option character when '?' is returned

Handling Errors with getopt

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

int main(int argc, char *argv[]) {
    int opt;

    // Suppress automatic error messages
    opterr = 0;

    while ((opt = getopt(argc, argv, ":a:b")) != -1) {
        switch (opt) {
            case 'a':
                printf("Option -a with value: %s\n", optarg);
                break;
            case 'b':
                printf("Option -b\n");
                break;
            case ':':
                // Missing argument (when optstring starts with ':')
                fprintf(stderr, "Option -%c requires an argument\n", optopt);
                return 1;
            case '?':
                // Unknown option
                fprintf(stderr, "Unknown option: -%c\n", optopt);
                return 1;
        }
    }

    return 0;
}

Using getopt_long for Long Options

The getopt_long() function supports both short and long options (e.g., --help).

Basic getopt_long Usage

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>  // For getopt_long

int main(int argc, char *argv[]) {
    int opt;
    int verbose = 0;
    int debug = 0;
    char *config_file = NULL;
    int port = 8080;

    // Define long options
    static struct option long_options[] = {
        {"verbose", no_argument,       NULL, 'v'},
        {"debug",   no_argument,       NULL, 'd'},
        {"config",  required_argument, NULL, 'c'},
        {"port",    required_argument, NULL, 'p'},
        {"help",    no_argument,       NULL, 'h'},
        {NULL,      0,                 NULL, 0}   // Terminator
    };

    while ((opt = getopt_long(argc, argv, "vdc:p:h",
                               long_options, NULL)) != -1) {
        switch (opt) {
            case 'v':
                verbose = 1;
                break;
            case 'd':
                debug = 1;
                break;
            case 'c':
                config_file = optarg;
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'h':
                printf("Usage: %s [options]\n", argv[0]);
                printf("Options:\n");
                printf("  -v, --verbose    Enable verbose output\n");
                printf("  -d, --debug      Enable debug mode\n");
                printf("  -c, --config     Config file path\n");
                printf("  -p, --port       Port number (default: 8080)\n");
                printf("  -h, --help       Show this help\n");
                return 0;
            case '?':
                return 1;
        }
    }

    printf("Configuration:\n");
    printf("  Verbose: %s\n", verbose ? "enabled" : "disabled");
    printf("  Debug: %s\n", debug ? "enabled" : "disabled");
    printf("  Config: %s\n", config_file ? config_file : "(default)");
    printf("  Port: %d\n", port);

    return 0;
}

struct option Fields

struct option {
    const char *name;     // Long option name
    int         has_arg;  // Argument requirement
    int        *flag;     // Flag pointer (or NULL)
    int         val;      // Value to return (or store in *flag)
};

has_arg Values

ConstantValueMeaning
no_argument0Option takes no argument
required_argument1Option requires an argument
optional_argument2Option has optional argument

Using Flag Pointer

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

int main(int argc, char *argv[]) {
    int verbose_flag = 0;
    int debug_flag = 0;
    int opt;

    static struct option long_options[] = {
        // When these are used, the flag is set directly
        {"verbose", no_argument, &verbose_flag, 1},
        {"debug",   no_argument, &debug_flag,   1},
        {"quiet",   no_argument, &verbose_flag, 0},

        // These return the val character
        {"file",    required_argument, NULL, 'f'},
        {"help",    no_argument,       NULL, 'h'},
        {NULL,      0,                 NULL, 0}
    };

    int option_index = 0;

    while ((opt = getopt_long(argc, argv, "f:h",
                               long_options, &option_index)) != -1) {
        switch (opt) {
            case 0:
                // Flag was set automatically
                printf("Option %s was set\n",
                       long_options[option_index].name);
                break;
            case 'f':
                printf("File: %s\n", optarg);
                break;
            case 'h':
                printf("Help message\n");
                return 0;
            case '?':
                return 1;
        }
    }

    printf("Verbose: %d, Debug: %d\n", verbose_flag, debug_flag);

    return 0;
}

Building Command Line Utilities

Complete Example: File Processing Tool

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

#define VERSION "1.0.0"
#define BUFFER_SIZE 4096

// Configuration structure
typedef struct {
    int verbose;
    int line_numbers;
    int count_only;
    char *input_file;
    char *output_file;
    char *search_pattern;
} Config;

void print_version(void) {
    printf("fileproc version %s\n", VERSION);
}

void print_usage(const char *program) {
    printf("Usage: %s [OPTIONS] [FILE]\n\n", program);
    printf("A file processing utility.\n\n");
    printf("Options:\n");
    printf("  -v, --verbose      Enable verbose output\n");
    printf("  -n, --number       Show line numbers\n");
    printf("  -c, --count        Count matching lines only\n");
    printf("  -p, --pattern=PAT  Search for pattern\n");
    printf("  -o, --output=FILE  Output file (default: stdout)\n");
    printf("  -h, --help         Display this help\n");
    printf("  -V, --version      Display version\n");
    printf("\nExamples:\n");
    printf("  %s -n file.txt\n", program);
    printf("  %s --pattern=hello input.txt -o output.txt\n", program);
}

int parse_args(int argc, char *argv[], Config *config) {
    static struct option long_options[] = {
        {"verbose", no_argument,       NULL, 'v'},
        {"number",  no_argument,       NULL, 'n'},
        {"count",   no_argument,       NULL, 'c'},
        {"pattern", required_argument, NULL, 'p'},
        {"output",  required_argument, NULL, 'o'},
        {"help",    no_argument,       NULL, 'h'},
        {"version", no_argument,       NULL, 'V'},
        {NULL,      0,                 NULL, 0}
    };

    int opt;

    // Initialize defaults
    memset(config, 0, sizeof(Config));

    while ((opt = getopt_long(argc, argv, "vncp:o:hV",
                               long_options, NULL)) != -1) {
        switch (opt) {
            case 'v':
                config->verbose = 1;
                break;
            case 'n':
                config->line_numbers = 1;
                break;
            case 'c':
                config->count_only = 1;
                break;
            case 'p':
                config->search_pattern = optarg;
                break;
            case 'o':
                config->output_file = optarg;
                break;
            case 'h':
                print_usage(argv[0]);
                exit(0);
            case 'V':
                print_version();
                exit(0);
            case '?':
                return -1;
        }
    }

    // Get input file from remaining arguments
    if (optind < argc) {
        config->input_file = argv[optind];
    }

    return 0;
}

int process_file(const Config *config) {
    FILE *input = stdin;
    FILE *output = stdout;
    char buffer[BUFFER_SIZE];
    int line_num = 0;
    int match_count = 0;

    // Open input file
    if (config->input_file) {
        input = fopen(config->input_file, "r");
        if (!input) {
            perror("Error opening input file");
            return -1;
        }
        if (config->verbose) {
            fprintf(stderr, "Reading from: %s\n", config->input_file);
        }
    }

    // Open output file
    if (config->output_file) {
        output = fopen(config->output_file, "w");
        if (!output) {
            perror("Error opening output file");
            if (input != stdin) fclose(input);
            return -1;
        }
        if (config->verbose) {
            fprintf(stderr, "Writing to: %s\n", config->output_file);
        }
    }

    // Process lines
    while (fgets(buffer, sizeof(buffer), input)) {
        line_num++;

        // Check pattern match
        int matches = 1;
        if (config->search_pattern) {
            matches = (strstr(buffer, config->search_pattern) != NULL);
        }

        if (matches) {
            match_count++;
            if (!config->count_only) {
                if (config->line_numbers) {
                    fprintf(output, "%6d: %s", line_num, buffer);
                } else {
                    fputs(buffer, output);
                }
            }
        }
    }

    // Print count if requested
    if (config->count_only) {
        fprintf(output, "%d\n", match_count);
    }

    if (config->verbose) {
        fprintf(stderr, "Processed %d lines, %d matches\n",
                line_num, match_count);
    }

    // Cleanup
    if (input != stdin) fclose(input);
    if (output != stdout) fclose(output);

    return 0;
}

int main(int argc, char *argv[]) {
    Config config;

    if (parse_args(argc, argv, &config) != 0) {
        print_usage(argv[0]);
        return 1;
    }

    return process_file(&config) == 0 ? 0 : 1;
}

Environment Variables

In addition to command line arguments, programs can access environment variables.

Third Argument to main

#include <stdio.h>

int main(int argc, char *argv[], char *envp[]) {
    // Print all environment variables
    for (int i = 0; envp[i] != NULL; i++) {
        printf("%s\n", envp[i]);
    }

    return 0;
}

Using getenv

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

int main(int argc, char *argv[]) {
    // Get specific environment variables
    char *home = getenv("HOME");
    char *path = getenv("PATH");
    char *user = getenv("USER");

    printf("HOME: %s\n", home ? home : "(not set)");
    printf("USER: %s\n", user ? user : "(not set)");
    printf("PATH: %s\n", path ? path : "(not set)");

    // Get custom environment variable
    char *my_var = getenv("MY_CONFIG");
    if (my_var) {
        printf("MY_CONFIG: %s\n", my_var);
    }

    return 0;
}

Setting Environment Variables

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

int main(void) {
    // Set environment variable
    // setenv(name, value, overwrite)
    if (setenv("MY_VAR", "hello", 1) != 0) {
        perror("setenv failed");
        return 1;
    }

    printf("MY_VAR = %s\n", getenv("MY_VAR"));

    // putenv uses the string directly (careful with memory)
    static char env_string[] = "ANOTHER_VAR=world";
    if (putenv(env_string) != 0) {
        perror("putenv failed");
        return 1;
    }

    // Unset environment variable
    if (unsetenv("MY_VAR") != 0) {
        perror("unsetenv failed");
    }

    return 0;
}

Best Practices

1. Validate Arguments

int main(int argc, char *argv[]) {
    // Check minimum arguments
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 1;
    }

    // Validate argument values
    int num = atoi(argv[1]);
    if (num <= 0 || num > 1000) {
        fprintf(stderr, "Error: Number must be between 1 and 1000\n");
        return 1;
    }

    return 0;
}

2. Provide Usage Information

void print_usage(const char *program) {
    fprintf(stderr, "Usage: %s [OPTIONS] FILE...\n", program);
    fprintf(stderr, "\nOptions:\n");
    fprintf(stderr, "  -h, --help     Show this help message\n");
    fprintf(stderr, "  -v, --version  Show version information\n");
    fprintf(stderr, "  -o FILE        Output file\n");
    fprintf(stderr, "\nExamples:\n");
    fprintf(stderr, "  %s input.txt\n", program);
    fprintf(stderr, "  %s -o output.txt input.txt\n", program);
}

3. Use Standard Exit Codes

#include <stdlib.h>

// Exit codes
#define EXIT_SUCCESS 0      // Successful execution
#define EXIT_FAILURE 1      // General error
#define EXIT_USAGE   2      // Command line usage error

int main(int argc, char *argv[]) {
    if (argc < 2) {
        print_usage(argv[0]);
        return EXIT_USAGE;
    }

    // ... process ...

    return EXIT_SUCCESS;
}

4. Support Standard Options

Follow conventions:

  • •-h or --help for help
  • •-v or --version for version
  • •- for reading from stdin
  • •-- to indicate end of options

5. Handle Errors Gracefully

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Error: Expected exactly one argument\n");
        fprintf(stderr, "Try '%s --help' for more information\n", argv[0]);
        return 1;
    }

    FILE *file = fopen(argv[1], "r");
    if (!file) {
        fprintf(stderr, "Error: Cannot open '%s': ", argv[1]);
        perror(NULL);
        return 1;
    }

    // Process file...
    fclose(file);
    return 0;
}

Common Use Cases

1. Calculator

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

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

    double a = atof(argv[1]);
    double b = atof(argv[3]);
    char op = argv[2][0];
    double result;

    switch (op) {
        case '+': result = a + b; break;
        case '-': result = a - b; break;
        case 'x':
        case '*': result = a * b; break;
        case '/':
            if (b == 0) {
                fprintf(stderr, "Error: Division by zero\n");
                return 1;
            }
            result = a / b;
            break;
        default:
            fprintf(stderr, "Error: Unknown operator '%c'\n", op);
            return 1;
    }

    printf("%.2f\n", result);
    return 0;
}

2. Word Counter

#include <stdio.h>
#include <ctype.h>

int main(int argc, char *argv[]) {
    FILE *file = stdin;

    if (argc > 1) {
        file = fopen(argv[1], "r");
        if (!file) {
            perror(argv[1]);
            return 1;
        }
    }

    int lines = 0, words = 0, chars = 0;
    int in_word = 0;
    int c;

    while ((c = fgetc(file)) != EOF) {
        chars++;
        if (c == '\n') lines++;
        if (isspace(c)) {
            in_word = 0;
        } else if (!in_word) {
            in_word = 1;
            words++;
        }
    }

    printf("%7d %7d %7d", lines, words, chars);
    if (argc > 1) {
        printf(" %s", argv[1]);
        fclose(file);
    }
    printf("\n");

    return 0;
}

3. File Copier

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

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
        return 1;
    }

    FILE *src = fopen(argv[1], "rb");
    if (!src) {
        perror(argv[1]);
        return 1;
    }

    FILE *dst = fopen(argv[2], "wb");
    if (!dst) {
        perror(argv[2]);
        fclose(src);
        return 1;
    }

    char buffer[4096];
    size_t bytes;

    while ((bytes = fread(buffer, 1, sizeof(buffer), src)) > 0) {
        fwrite(buffer, 1, bytes, dst);
    }

    fclose(src);
    fclose(dst);

    printf("Copied '%s' to '%s'\n", argv[1], argv[2]);
    return 0;
}

Error Handling

Comprehensive Error Handling Example

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

#define MAX_FILES 100

typedef enum {
    ERR_NONE = 0,
    ERR_USAGE,
    ERR_ARGS,
    ERR_FILE,
    ERR_MEMORY,
    ERR_OVERFLOW
} ErrorCode;

const char *error_messages[] = {
    "No error",
    "Usage error",
    "Invalid argument",
    "File error",
    "Memory allocation failed",
    "Integer overflow"
};

void report_error(ErrorCode code, const char *detail) {
    fprintf(stderr, "Error: %s", error_messages[code]);
    if (detail) {
        fprintf(stderr, ": %s", detail);
    }
    fprintf(stderr, "\n");
}

long safe_strtol(const char *str, ErrorCode *err) {
    char *endptr;
    errno = 0;

    long val = strtol(str, &endptr, 10);

    if (errno == ERANGE) {
        *err = ERR_OVERFLOW;
        return 0;
    }

    if (*endptr != '\0' || str == endptr) {
        *err = ERR_ARGS;
        return 0;
    }

    *err = ERR_NONE;
    return val;
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        report_error(ERR_USAGE, "No arguments provided");
        fprintf(stderr, "Usage: %s <number>\n", argv[0]);
        return ERR_USAGE;
    }

    ErrorCode err;
    long num = safe_strtol(argv[1], &err);

    if (err != ERR_NONE) {
        report_error(err, argv[1]);
        return err;
    }

    printf("Number: %ld\n", num);
    return 0;
}

Summary

Key Concepts

  1. •

    argc and argv: The primary mechanism for receiving command line arguments

    • •argc counts arguments including the program name
    • •argv is an array of strings containing the arguments
    • •argv[argc] is always NULL
  2. •

    Argument Conversion: All arguments are strings

    • •Use atoi(), atof() for simple conversion
    • •Use strtol(), strtod() for robust conversion with error checking
  3. •

    Option Parsing:

    • •Manual parsing for simple needs
    • •getopt() for short options (POSIX standard)
    • •getopt_long() for both short and long options (GNU extension)
  4. •

    Best Practices:

    • •Always validate input
    • •Provide usage information
    • •Use standard exit codes
    • •Support -h/--help and --version
    • •Handle errors gracefully

Common Patterns

// Basic argument check
if (argc < 2) {
    fprintf(stderr, "Usage: %s <arg>\n", argv[0]);
    return 1;
}

// Loop through arguments
for (int i = 1; i < argc; i++) {
    process(argv[i]);
}

// Use getopt for options
while ((opt = getopt(argc, argv, "hvo:")) != -1) {
    // handle options
}
// Process remaining: argv[optind] to argv[argc-1]

When to Use Each Approach

ApproachUse Case
Manual parsingSimple programs, few arguments
getoptStandard UNIX utilities, short options
getopt_longModern CLI tools, both short and long options
Custom parserComplex argument syntax, specific requirements

References

  1. •POSIX getopt specification
  2. •GNU C Library Manual - Parsing Program Arguments
  3. •The Linux Command Line by William Shotts
  4. •Unix System Programming by Keith Haviland
  5. •Advanced Programming in the UNIX Environment by W. Richard Stevens
README - C Programming Tutorial | DeepML