Skip to main content

argc & argv - Command-Line Arguments

Command-line arguments allow programs to receive input directly when executed. This is essential for creating flexible, scriptable programs.

Understanding argc and argv

The main Function Signature

int main(int argc, char *argv[])
Or equivalently:
int main(int argc, char **argv)
Parameters:
  • argc (argument count) - Number of command-line arguments, including the program name
  • argv (argument vector) - Array of strings (character pointers) containing the arguments

How It Works

When you run a program from the command line:
./program arg1 arg2 arg3
Memory representation:
argc = 4

argv[0] → "./program"
argv[1] → "arg1"
argv[2] → "arg2"
argv[3] → "arg3"
argv[4] → NULL (terminator)
argv[0] always contains the program name (or path). Actual arguments start at argv[1].

Basic Examples

0-whatsmyname.c - Print Program Name

#include <stdio.h>

/**
 * main - prints the programs name, followed by a new line
 * @argc: int
 * @argv: list
 * Return: 0
 */

int main(int argc, char const *argv[])
{
    (void)argc;
    printf("%s\n", argv[0]);
    return (0);
}
How it works:
  • (void)argc - Tells compiler we intentionally don’t use argc
  • argv[0] - Always contains the program name
  • No need to check argc since argv[0] always exists
Usage:
$ ./whatsmyname
./whatsmyname

$ /usr/bin/whatsmyname
/usr/bin/whatsmyname

1-args.c - Count Arguments

#include <stdio.h>

/**
 * main - prints the number of arguments passed into it
 * @argc: int
 * @argv: list
 * Return: 0
 */

int main(int argc, char const *argv[])
{
    (void)argv;
    printf("%i\n", argc - 1);
    return (0);
}
Why argc - 1?
  • argc includes the program name
  • Actual user arguments = total arguments - 1
Examples:
$ ./args
0

$ ./args hello
1

$ ./args hello world test
3

2-args.c - Print All Arguments

#include <stdio.h>

/**
 * main - prints the number of arguments passed into the program
 * @argc: int
 * @argv: list
 * Return: 0
 */

int main(int argc, char const *argv[])
{
    int i = 0;
    
    while (argc--)
    {
        printf("%s\n", argv[i]);
        i++;
    }
    
    return (0);
}
Algorithm:
  1. Start with i = 0
  2. Print argv[i]
  3. Decrement argc and increment i
  4. Continue until argc reaches 0
Example:
$ ./args one two three
./args
one
two
three
Alternative approach:
for (int i = 0; i < argc; i++)
    printf("%s\n", argv[i]);

Processing Arguments

3-mul.c - Multiply Two Numbers

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

/**
 * main - multiply two numbers
 * @argc: int
 * @argv: list
 * Return: 0
 */

int main(int argc, char const *argv[])
{
    (void)argc;
    
    if (argc != 3)
    {
        printf("Error\n");
        return (1);
    }
    printf("%i\n", atoi(argv[1]) * atoi(argv[2]));
    return (0);
}
Key concepts:
  1. Argument validation: Check if exactly 2 arguments are provided (argc = 3 including program name)
  2. String to integer conversion: Use atoi() to convert string arguments to integers
  3. Error handling: Return 1 on error (convention for failure)
Usage:
$ ./mul 5 7
35

$ ./mul 10 -3
-30

$ ./mul 5
Error

$ ./mul 5 7 9
Error
atoi() has limitations:
  • Returns 0 for non-numeric strings
  • No error indication
  • Cannot distinguish between “0” and invalid input
atoi("42")    // Returns 42
atoi("abc")   // Returns 0 (no error!)
atoi("0")     // Returns 0 (valid)
For robust code, use strtol() instead.

4-add.c - Add Positive Numbers

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

/**
 * isInteger - checks if s is an integer
 * @s: string to check
 * Return: 0 or 1
 */

int isInteger(const char *s)
{
    int i = 0;
    while (s[i] != '\0')
    {
        if (s[i] < '0' || s[i] > '9')
            return (1);
        i++;
    }
    return (0);
}

/**
 * main - adds positive numbers
 * @argc: int
 * @argv: list
 * Return: 0
 */

int main(int argc, char const *argv[])
{
    int sum = 0;
    while (--argc)
    {
        if (isInteger(argv[argc]))
        {
            printf("Error\n");
            return (1);
        }
        sum += atoi(argv[argc]);
    }
    
    printf("%i\n", sum);
    
    return (0);
}
Features:
  1. Input validation: isInteger() checks if string contains only digits
  2. Variable arguments: Handles any number of arguments
  3. Error handling: Returns error if non-numeric input found
How it works:
  • Start with sum = 0
  • Process arguments from last to first (--argc)
  • Skip argv[0] (program name)
  • Validate each argument is numeric
  • Add to running sum
Examples:
$ ./add 1 2 3 4 5
15

$ ./add 10 20 30
60

$ ./add
0

$ ./add 1 2 x 4
Error
Processing arguments backwards (--argc) is a common pattern:
while (--argc)  // Stops when argc reaches 0
    process(argv[argc]);
This automatically skips argv[0] (program name).

Advanced Argument Parsing

Handling Options and Flags

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

int main(int argc, char *argv[])
{
    int verbose = 0;
    int count = 0;
    
    for (int i = 1; i < argc; i++)
    {
        if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0)
        {
            verbose = 1;
        }
        else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--count") == 0)
        {
            if (i + 1 < argc)
            {
                count = atoi(argv[i + 1]);
                i++;  // Skip next argument (already processed)
            }
        }
        else
        {
            printf("Unknown option: %s\n", argv[i]);
        }
    }
    
    if (verbose)
        printf("Verbose mode enabled\n");
    
    printf("Count: %d\n", count);
    
    return (0);
}
Usage:
$ ./program -v -c 10
Verbose mode enabled
Count: 10

$ ./program --count 5
Count: 5

Safe String to Number Conversion

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

int safe_atoi(const char *str, int *result)
{
    char *endptr;
    long val;
    
    errno = 0;
    val = strtol(str, &endptr, 10);
    
    // Check for various errors
    if (errno != 0 || endptr == str || *endptr != '\0')
        return (0);  // Conversion failed
    
    // Check for integer overflow
    if (val > INT_MAX || val < INT_MIN)
        return (0);
    
    *result = (int)val;
    return (1);  // Success
}

int main(int argc, char *argv[])
{
    int num;
    
    if (argc != 2)
    {
        printf("Usage: %s <number>\n", argv[0]);
        return (1);
    }
    
    if (!safe_atoi(argv[1], &num))
    {
        printf("Error: '%s' is not a valid number\n", argv[1]);
        return (1);
    }
    
    printf("Number: %d\n", num);
    return (0);
}

Common Patterns

1. Processing Files

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("Usage: %s <file1> [file2] ...\n", argv[0]);
        return (1);
    }
    
    for (int i = 1; i < argc; i++)
    {
        printf("Processing file: %s\n", argv[i]);
        // Open and process file
    }
    
    return (0);
}

2. Environment Variables

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

3. Help Message

void print_usage(char *program_name)
{
    printf("Usage: %s [OPTIONS] [FILES]\n", program_name);
    printf("\nOptions:\n");
    printf("  -h, --help     Show this help message\n");
    printf("  -v, --verbose  Enable verbose output\n");
    printf("  -o FILE        Output to FILE\n");
}

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        print_usage(argv[0]);
        return (1);
    }
    
    // Process arguments...
    
    return (0);
}

Common Pitfalls

1. Assuming Arguments Exist

// ❌ BAD - may crash
int main(int argc, char *argv[])
{
    printf("%s\n", argv[1]);  // What if argc == 1?
    return (0);
}

// ✅ GOOD - check first
int main(int argc, char *argv[])
{
    if (argc > 1)
        printf("%s\n", argv[1]);
    else
        printf("No arguments provided\n");
    return (0);
}

2. Off-by-One Errors

// ❌ BAD - counts program name as argument
int arg_count = argc;

// ✅ GOOD - exclude program name
int arg_count = argc - 1;

3. Not Validating Input

// ❌ BAD - no validation
int x = atoi(argv[1]);
int result = 100 / x;  // Division by zero if argv[1] is "0" or "abc"

// ✅ GOOD - validate
int x = atoi(argv[1]);
if (x == 0)
{
    printf("Error: divisor cannot be zero\n");
    return (1);
}
int result = 100 / x;

4. Modifying argv Strings

// ❌ BAD - argv strings may be in read-only memory
argv[1][0] = 'X';

// ✅ GOOD - copy to modifiable buffer
char buffer[100];
strcpy(buffer, argv[1]);
buffer[0] = 'X';

Best Practices

  1. Always validate argc before accessing argv
    if (argc < required_args)
        return error;
    
  2. Provide helpful error messages
    printf("Usage: %s <input> <output>\n", argv[0]);
    
  3. Use descriptive variable names
    char *input_file = argv[1];
    char *output_file = argv[2];
    
  4. Validate all inputs
    • Check for required number of arguments
    • Validate argument format
    • Check for valid ranges
  5. Return appropriate exit codes
    return (0);   // Success
    return (1);   // General error
    return (2);   // Misuse of command
    

Key Takeaways

  • argc counts all arguments including program name
  • argv[0] is always the program name
  • Arguments are passed as strings - convert with atoi() or strtol()
  • Always validate argc before accessing argv[i]
  • atoi() returns 0 for invalid input without error indication
  • Use strtol() for robust string-to-number conversion
  • Return 0 for success, non-zero for errors
  • Process arguments from index 1 (skip program name)

Build docs developers (and LLMs) love