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:
argc = 3
|
argv[argc] = NULL
#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;
}
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.
#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;
}
#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;
}
#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.
#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;
}
#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.
#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 pow 2 10
./calculator sin 1.57
12.50 + 7.30 = 19.80
2.00 ^ 10.00 = 1024.00
sin(1.57) = 1.0000
Common Mistakes and Best Practices
Command Line Argument Best Practices:
- Always validate argc: Check argument count before accessing argv elements
- Provide helpful error messages: Include usage instructions in error output
- Use standard conventions: Follow POSIX/GNU conventions for option naming
- Implement -h/--help: Always include a help option that explains usage
- Validate input types: Check that arguments are valid numbers, files exist, etc.
- Use meaningful names: Choose option names that clearly indicate their purpose
- Support both short and long options: Allow -v and --verbose for user convenience
- Return proper exit codes: Use different return values for different error conditions
- Document thoroughly: Provide clear documentation for all options
- 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 alwaysNULL- Always validate
argcbefore accessingargvelements to avoid segmentation faults - Use
atoi(),atof(), orstrtol()to convert string arguments to numbers - Implement
-hor--helpoptions to display usage instructions - Follow standard conventions: single-letter options (
-v) and long options (--verbose) - Use
getopt()(POSIX) orgetopt_long()(GNU) for robust option parsing - Return meaningful exit codes:
0for 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