C Programming Command Line
Essential Concepts

C Command Line Arguments - Complete Guide

Master command line arguments in C with detailed explanations of argc and argv, practical examples for parsing inputs, and best practices for building robust CLI applications.

argc & argv

Core parameters

CLI Applications

Real-world usage

Argument Parsing

Advanced techniques

Introduction to Command Line Arguments

Command line arguments allow users to pass information to a C program when it starts executing. This makes programs more flexible, configurable, and suitable for automation and scripting.

Why Use Command Line Arguments?
  • Flexibility: Configure program behavior without recompiling
  • Automation: Script and automate program execution
  • User-Friendly: Provide intuitive interface for power users
  • Batch Processing: Process multiple files or datasets
  • Portability: Standard across all operating systems
Common Use Cases
  • File Processing: Pass input/output filenames
  • Configuration: Set program options and flags
  • Data Input: Provide numerical parameters
  • Mode Selection: Choose between operation modes
  • Debugging: Enable verbose output or debugging

Key Components

The main() function in C can accept two parameters: argc (argument count) and argv (argument vector). These parameters give your program access to command line inputs, making it interactive and configurable.

argc and argv Parameters

The argc (argument count) and argv (argument vector) are the two parameters that enable command line argument processing in C programs.

Standard main() Function Signature:
int main(int argc, char *argv[]) { // Program code here return 0; }

Parameter Details:

Parameter Type Description Example Value
argc int Argument count - number of command line arguments For command: program arg1 arg2, argc = 3
argv char *[] Argument vector - array of strings containing arguments argv[0] = "program", argv[1] = "arg1", argv[2] = "arg2"

Memory Layout Visualization:

argv[0]
"program.exe"
argv[1]
"input.txt"
argv[2]
"output.txt"
argv[3]
NULL
argc = 3 | argv[argc] = NULL
Basic Example: Printing All Arguments
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Total arguments: %d\n", argc);
    printf("Program name: %s\n", argv[0]);
    
    printf("\nAll arguments:\n");
    for(int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    
    printf("\nArguments (excluding program name):\n");
    for(int i = 1; i < argc; i++) {
        printf("Argument %d: %s\n", i, argv[i]);
    }
    
    return 0;
}
$
./myprogram file1.txt file2.txt -v

Output:
Total arguments: 4
Program name: ./myprogram

All arguments:
argv[0] = ./myprogram
argv[1] = file1.txt
argv[2] = file2.txt
argv[3] = -v

Arguments (excluding program name):
Argument 1: file1.txt
Argument 2: file2.txt
Argument 3: -v

Parsing Command Line Arguments

Proper parsing of command line arguments involves validation, type conversion, and handling different argument formats like flags, options, and values.

Example 1: Basic Argument Validation
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    // Check minimum arguments
    if(argc < 3) {
        printf("Usage: %s <number1> <number2>\n", argv[0]);
        printf("Example: %s 10 20\n", argv[0]);
        return 1; // Return error code
    }
    
    // Convert arguments to integers
    int num1 = atoi(argv[1]);
    int num2 = atoi(argv[2]);
    
    // Perform calculations
    printf("Number 1: %d\n", num1);
    printf("Number 2: %d\n", num2);
    printf("Sum: %d\n", num1 + num2);
    printf("Difference: %d\n", num1 - num2);
    printf("Product: %d\n", num1 * num2);
    
    if(num2 != 0) {
        printf("Quotient: %.2f\n", (float)num1 / num2);
    } else {
        printf("Cannot divide by zero!\n");
    }
    
    return 0;
}
Example 2: File Processing with Arguments
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    // Validate arguments
    if(argc != 3) {
        fprintf(stderr, "Error: Invalid number of arguments.\n");
        fprintf(stderr, "Usage: %s <input_file> <output_file>\n", argv[0]);
        return 1;
    }
    
    char *input_filename = argv[1];
    char *output_filename = argv[2];
    
    // Open input file
    FILE *input_file = fopen(input_filename, "r");
    if(input_file == NULL) {
        fprintf(stderr, "Error: Cannot open input file '%s'\n", input_filename);
        return 2;
    }
    
    // Open output file
    FILE *output_file = fopen(output_filename, "w");
    if(output_file == NULL) {
        fprintf(stderr, "Error: Cannot create output file '%s'\n", output_filename);
        fclose(input_file);
        return 3;
    }
    
    printf("Processing file: %s -> %s\n", input_filename, output_filename);
    
    // Read and process file
    int char_count = 0, line_count = 0;
    char ch;
    
    while((ch = fgetc(input_file)) != EOF) {
        fputc(ch, output_file);
        char_count++;
        
        if(ch == '\n') {
            line_count++;
        }
    }
    
    // Close files
    fclose(input_file);
    fclose(output_file);
    
    // Display statistics
    printf("File processing complete!\n");
    printf("Characters copied: %d\n", char_count);
    printf("Lines copied: %d\n", line_count);
    
    return 0;
}
Example 3: Handling Flags and Options
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(int argc, char *argv[]) {
    // Default values
    int verbose = 0;
    int uppercase = 0;
    char *input_file = NULL;
    char *output_file = NULL;
    
    // Parse command line arguments
    for(int i = 1; i < argc; i++) {
        if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
            verbose = 1;
            printf("Verbose mode enabled\n");
        }
        else if(strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--uppercase") == 0) {
            uppercase = 1;
            if(verbose) printf("Uppercase mode enabled\n");
        }
        else if(strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--input") == 0) {
            if(i + 1 < argc) {
                input_file = argv[++i];
                if(verbose) printf("Input file: %s\n", input_file);
            } else {
                fprintf(stderr, "Error: -i requires a filename\n");
                return 1;
            }
        }
        else if(strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) {
            if(i + 1 < argc) {
                output_file = argv[++i];
                if(verbose) printf("Output file: %s\n", output_file);
            } else {
                fprintf(stderr, "Error: -o requires a filename\n");
                return 1;
            }
        }
        else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            printf("Usage: %s [options]\n", argv[0]);
            printf("Options:\n");
            printf("  -v, --verbose    Enable verbose output\n");
            printf("  -u, --uppercase  Convert text to uppercase\n");
            printf("  -i, --input FILE Specify input file\n");
            printf("  -o, --output FILE Specify output file\n");
            printf("  -h, --help       Display this help message\n");
            return 0;
        }
        else {
            fprintf(stderr, "Error: Unknown option '%s'\n", argv[i]);
            fprintf(stderr, "Use -h for help\n");
            return 1;
        }
    }
    
    // Validate required arguments
    if(input_file == NULL || output_file == NULL) {
        fprintf(stderr, "Error: Input and output files are required\n");
        fprintf(stderr, "Use -h for help\n");
        return 1;
    }
    
    // Process files
    FILE *in = fopen(input_file, "r");
    FILE *out = fopen(output_file, "w");
    
    if(in == NULL || out == NULL) {
        fprintf(stderr, "Error: Cannot open files\n");
        return 1;
    }
    
    char ch;
    int char_count = 0;
    
    while((ch = fgetc(in)) != EOF) {
        if(uppercase && islower(ch)) {
            ch = toupper(ch);
        }
        fputc(ch, out);
        char_count++;
    }
    
    fclose(in);
    fclose(out);
    
    if(verbose) {
        printf("Processing complete!\n");
        printf("Characters processed: %d\n", char_count);
        printf("Input: %s\n", input_file);
        printf("Output: %s\n", output_file);
        printf("Uppercase mode: %s\n", uppercase ? "Yes" : "No");
    }
    
    return 0;
}

Advanced Argument Processing

Advanced techniques include using getopt() for standard argument parsing, handling multiple argument types, and creating robust CLI applications.

Using getopt() for Standard Argument Parsing
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>  // For getopt()

int main(int argc, char *argv[]) {
    int opt;
    int verbose = 0;
    int count = 1;
    char *input_file = NULL;
    char *output_file = NULL;
    
    // Parse options using getopt
    while((opt = getopt(argc, argv, "vhc:i:o:")) != -1) {
        switch(opt) {
            case 'v':
                verbose = 1;
                break;
            case 'h':
                printf("Usage: %s [options]\n", argv[0]);
                printf("Options:\n");
                printf("  -v            Verbose output\n");
                printf("  -h            Show this help message\n");
                printf("  -c COUNT      Repeat count (default: 1)\n");
                printf("  -i FILE       Input file\n");
                printf("  -o FILE       Output file\n");
                return 0;
            case 'c':
                count = atoi(optarg);
                if(count <= 0) {
                    fprintf(stderr, "Error: Count must be positive\n");
                    return 1;
                }
                break;
            case 'i':
                input_file = optarg;
                break;
            case 'o':
                output_file = optarg;
                break;
            case '?':
                fprintf(stderr, "Unknown option: %c\n", optopt);
                return 1;
            default:
                fprintf(stderr, "Unexpected error\n");
                return 1;
        }
    }
    
    // Handle non-option arguments
    if(optind < argc) {
        printf("Additional arguments:\n");
        for(int i = optind; i < argc; i++) {
            printf("  %s\n", argv[i]);
        }
    }
    
    // Display parsed values
    if(verbose) {
        printf("Parsed options:\n");
        printf("  Verbose: %s\n", verbose ? "Yes" : "No");
        printf("  Count: %d\n", count);
        printf("  Input file: %s\n", input_file ? input_file : "(not specified)");
        printf("  Output file: %s\n", output_file ? output_file : "(not specified)");
    }
    
    // Process based on options
    for(int i = 0; i < count; i++) {
        if(verbose) {
            printf("Iteration %d/%d\n", i + 1, count);
        }
        // Your processing logic here
    }
    
    return 0;
}
Advanced: Argument Parsing with Structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Configuration structure
typedef struct {
    int verbose;
    int debug;
    int threads;
    char *input_file;
    char *output_file;
    char *mode;
} Config;

// Function to parse arguments into config structure
int parse_arguments(int argc, char *argv[], Config *config) {
    // Initialize with defaults
    config->verbose = 0;
    config->debug = 0;
    config->threads = 1;
    config->input_file = NULL;
    config->output_file = NULL;
    config->mode = "normal";
    
    for(int i = 1; i < argc; i++) {
        if(strcmp(argv[i], "-v") == 0) {
            config->verbose = 1;
        }
        else if(strcmp(argv[i], "-d") == 0) {
            config->debug = 1;
        }
        else if(strcmp(argv[i], "-t") == 0) {
            if(i + 1 < argc) {
                config->threads = atoi(argv[++i]);
                if(config->threads < 1) {
                    fprintf(stderr, "Error: Threads must be >= 1\n");
                    return 0;
                }
            } else {
                fprintf(stderr, "Error: -t requires thread count\n");
                return 0;
            }
        }
        else if(strcmp(argv[i], "-i") == 0) {
            if(i + 1 < argc) {
                config->input_file = argv[++i];
            } else {
                fprintf(stderr, "Error: -i requires filename\n");
                return 0;
            }
        }
        else if(strcmp(argv[i], "-o") == 0) {
            if(i + 1 < argc) {
                config->output_file = argv[++i];
            } else {
                fprintf(stderr, "Error: -o requires filename\n");
                return 0;
            }
        }
        else if(strcmp(argv[i], "-m") == 0) {
            if(i + 1 < argc) {
                config->mode = argv[++i];
            } else {
                fprintf(stderr, "Error: -m requires mode name\n");
                return 0;
            }
        }
        else if(strcmp(argv[i], "-h") == 0) {
            printf("Usage: %s [options]\n", argv[0]);
            printf("Options:\n");
            printf("  -v            Verbose mode\n");
            printf("  -d            Debug mode\n");
            printf("  -t THREADS    Number of threads\n");
            printf("  -i FILE       Input file\n");
            printf("  -o FILE       Output file\n");
            printf("  -m MODE       Processing mode\n");
            printf("  -h            This help message\n");
            return 0;
        }
        else {
            fprintf(stderr, "Error: Unknown option '%s'\n", argv[i]);
            return 0;
        }
    }
    
    // Validate required arguments
    if(config->input_file == NULL) {
        fprintf(stderr, "Error: Input file is required\n");
        return 0;
    }
    
    return 1; // Success
}

// Function to display configuration
void display_config(const Config *config) {
    printf("Configuration:\n");
    printf("  Verbose mode: %s\n", config->verbose ? "Yes" : "No");
    printf("  Debug mode: %s\n", config->debug ? "Yes" : "No");
    printf("  Threads: %d\n", config->threads);
    printf("  Input file: %s\n", config->input_file);
    printf("  Output file: %s\n", config->output_file ? config->output_file : "(none)");
    printf("  Mode: %s\n", config->mode);
}

int main(int argc, char *argv[]) {
    Config config;
    
    if(!parse_arguments(argc, argv, &config)) {
        fprintf(stderr, "Use -h for help\n");
        return 1;
    }
    
    if(config.verbose) {
        display_config(&config);
    }
    
    printf("Processing with configuration...\n");
    // Your processing logic here
    
    return 0;
}

Practical Applications

Command line arguments are used in various real-world applications. Here are practical examples demonstrating their utility.

Example: Calculator with Command Line Interface
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

void show_help(char *program_name) {
    printf("Command Line Calculator\n");
    printf("Usage: %s <operation> <operand1> [operand2]\n", program_name);
    printf("\nOperations:\n");
    printf("  add     a b     Add a and b\n");
    printf("  sub     a b     Subtract b from a\n");
    printf("  mul     a b     Multiply a and b\n");
    printf("  div     a b     Divide a by b\n");
    printf("  pow     a b     a raised to power b\n");
    printf("  sqrt    a       Square root of a\n");
    printf("  sin     a       Sine of a (radians)\n");
    printf("  cos     a       Cosine of a (radians)\n");
    printf("  help            Show this help\n");
    printf("\nExamples:\n");
    printf("  %s add 5 3       # Result: 8\n", program_name);
    printf("  %s sqrt 16       # Result: 4\n", program_name);
    printf("  %s pow 2 8       # Result: 256\n", program_name);
}

int main(int argc, char *argv[]) {
    if(argc < 2) {
        show_help(argv[0]);
        return 1;
    }
    
    char *operation = argv[1];
    
    if(strcmp(operation, "help") == 0) {
        show_help(argv[0]);
        return 0;
    }
    
    // Validate argument count based on operation
    if((strcmp(operation, "sqrt") == 0 || strcmp(operation, "sin") == 0 || 
        strcmp(operation, "cos") == 0) && argc != 3) {
        fprintf(stderr, "Error: %s requires 1 operand\n", operation);
        printf("Example: %s %s 25\n", argv[0], operation);
        return 1;
    }
    else if(argc != 4) {
        fprintf(stderr, "Error: %s requires 2 operands\n", operation);
        printf("Example: %s %s 10 5\n", argv[0], operation);
        return 1;
    }
    
    // Parse operands
    double a = atof(argv[2]);
    double b = (argc == 4) ? atof(argv[3]) : 0;
    double result = 0;
    
    // Perform calculation
    if(strcmp(operation, "add") == 0) {
        result = a + b;
        printf("%.2f + %.2f = %.2f\n", a, b, result);
    }
    else if(strcmp(operation, "sub") == 0) {
        result = a - b;
        printf("%.2f - %.2f = %.2f\n", a, b, result);
    }
    else if(strcmp(operation, "mul") == 0) {
        result = a * b;
        printf("%.2f * %.2f = %.2f\n", a, b, result);
    }
    else if(strcmp(operation, "div") == 0) {
        if(b == 0) {
            fprintf(stderr, "Error: Division by zero\n");
            return 1;
        }
        result = a / b;
        printf("%.2f / %.2f = %.2f\n", a, b, result);
    }
    else if(strcmp(operation, "pow") == 0) {
        result = pow(a, b);
        printf("%.2f ^ %.2f = %.2f\n", a, b, result);
    }
    else if(strcmp(operation, "sqrt") == 0) {
        if(a < 0) {
            fprintf(stderr, "Error: Cannot calculate square root of negative number\n");
            return 1;
        }
        result = sqrt(a);
        printf("sqrt(%.2f) = %.2f\n", a, result);
    }
    else if(strcmp(operation, "sin") == 0) {
        result = sin(a);
        printf("sin(%.2f) = %.4f\n", a, result);
    }
    else if(strcmp(operation, "cos") == 0) {
        result = cos(a);
        printf("cos(%.2f) = %.4f\n", a, result);
    }
    else {
        fprintf(stderr, "Error: Unknown operation '%s'\n", operation);
        show_help(argv[0]);
        return 1;
    }
    
    return 0;
}
$
./calculator add 12.5 7.3
./calculator pow 2 10
./calculator sin 1.57

Output:
12.50 + 7.30 = 19.80
2.00 ^ 10.00 = 1024.00
sin(1.57) = 1.0000

Common Mistakes and Best Practices

Common Mistake 1: Not checking argc before accessing argv
// DANGEROUS: May cause segmentation fault printf("First argument: %s\n", argv[1]);
// SAFE: Always check argc first if(argc > 1) { printf("First argument: %s\n", argv[1]); }
Common Mistake 2: Not validating argument types
// RISKY: atoi() returns 0 for invalid input int value = atoi(argv[1]);
// BETTER: Use strtol() with error checking char *endptr; long value = strtol(argv[1], &endptr, 10); if(*endptr != '\0') { fprintf(stderr, "Error: Invalid number '%s'\n", argv[1]); return 1; }
Common Mistake 3: Not handling missing required arguments
// POOR: Assumes arguments exist FILE *fp = fopen(argv[1], "r");
// GOOD: Validate and provide helpful error if(argc < 2) { fprintf(stderr, "Error: Input filename required\n"); fprintf(stderr, "Usage: %s <filename>\n", argv[0]); return 1; }
Command Line Argument Best Practices:
  1. Always validate argc: Check argument count before accessing argv elements
  2. Provide helpful error messages: Include usage instructions in error output
  3. Use standard conventions: Follow POSIX/GNU conventions for option naming
  4. Implement -h/--help: Always include a help option that explains usage
  5. Validate input types: Check that arguments are valid numbers, files exist, etc.
  6. Use meaningful names: Choose option names that clearly indicate their purpose
  7. Support both short and long options: Allow -v and --verbose for user convenience
  8. Return proper exit codes: Use different return values for different error conditions
  9. Document thoroughly: Provide clear documentation for all options
  10. Test edge cases: Test with no arguments, invalid arguments, and boundary cases

Key Takeaways

  • Command line arguments are passed to main() via argc (count) and argv (array of strings)
  • argv[0] always contains the program name, argv[argc] is always NULL
  • Always validate argc before accessing argv elements to avoid segmentation faults
  • Use atoi(), atof(), or strtol() to convert string arguments to numbers
  • Implement -h or --help options to display usage instructions
  • Follow standard conventions: single-letter options (-v) and long options (--verbose)
  • Use getopt() (POSIX) or getopt_long() (GNU) for robust option parsing
  • Return meaningful exit codes: 0 for success, non-zero for errors
  • Provide clear error messages that guide users toward correct usage
  • Test your program with various argument combinations, including edge cases
Next Topics: We'll explore preprocessor directives and importance.