README
Command Line Arguments in C
Table of Contents
- •Introduction
- •Understanding argc and argv
- •How Command Line Arguments Work
- •Accessing Command Line Arguments
- •Converting String Arguments
- •Argument Parsing Techniques
- •Using getopt for Option Parsing
- •Using getopt_long for Long Options
- •Building Command Line Utilities
- •Environment Variables
- •Best Practices
- •Common Use Cases
- •Error Handling
- •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?
- •Flexibility: Allow users to control program behavior at runtime
- •Automation: Enable programs to be used in scripts and batch processing
- •Non-Interactive Operation: Programs can run without user intervention
- •Configuration: Pass settings and options without configuration files
- •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[]orchar **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
- •User Types Command: User enters a command in the terminal
- •Shell Parses Input: The shell splits the command by whitespace
- •OS Creates Process: Operating system creates a new process
- •Arguments Passed: OS passes arguments to the program's main function
- •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
| Function | Converts To | Error Handling | Header |
|---|---|---|---|
atoi() | int | None | stdlib.h |
atol() | long | None | stdlib.h |
atof() | double | None | stdlib.h |
strtol() | long | Yes (endptr, errno) | stdlib.h |
strtoll() | long long | Yes (endptr, errno) | stdlib.h |
strtoul() | unsigned long | Yes (endptr, errno) | stdlib.h |
strtod() | double | Yes (endptr, errno) | stdlib.h |
strtof() | float | Yes (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
| Character | Meaning |
|---|---|
a | Option '-a' with no argument |
a: | Option '-a' requires an argument |
a:: | Option '-a' has optional argument (GNU extension) |
: at start | Return ':' for missing argument instead of '?' |
getopt Global Variables
| Variable | Description |
|---|---|
optarg | Pointer to the argument of the current option |
optind | Index of the next element to be processed in argv |
opterr | If non-zero, getopt prints error messages (default: 1) |
optopt | Contains 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
| Constant | Value | Meaning |
|---|---|---|
no_argument | 0 | Option takes no argument |
required_argument | 1 | Option requires an argument |
optional_argument | 2 | Option 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:
- •
-hor--helpfor help - •
-vor--versionfor 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
- •
argc and argv: The primary mechanism for receiving command line arguments
- •
argccounts arguments including the program name - •
argvis an array of strings containing the arguments - •
argv[argc]is always NULL
- •
- •
Argument Conversion: All arguments are strings
- •Use
atoi(),atof()for simple conversion - •Use
strtol(),strtod()for robust conversion with error checking
- •Use
- •
Option Parsing:
- •Manual parsing for simple needs
- •
getopt()for short options (POSIX standard) - •
getopt_long()for both short and long options (GNU extension)
- •
Best Practices:
- •Always validate input
- •Provide usage information
- •Use standard exit codes
- •Support
-h/--helpand--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
| Approach | Use Case |
|---|---|
| Manual parsing | Simple programs, few arguments |
| getopt | Standard UNIX utilities, short options |
| getopt_long | Modern CLI tools, both short and long options |
| Custom parser | Complex argument syntax, specific requirements |
References
- •POSIX getopt specification
- •GNU C Library Manual - Parsing Program Arguments
- •The Linux Command Line by William Shotts
- •Unix System Programming by Keith Haviland
- •Advanced Programming in the UNIX Environment by W. Richard Stevens