Command-Line Argument Processing and String Manipulation in C

When processing command-line arguments in C, the main function receives two parameters: argc (argument count) and argv (array of argument strings). The following example demonstrates sorting these arguments lexicographically using qsort with a custom comparator:

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

int compare(const void *a, const void *b) {
    return strcmp(*(char **)a, *(char **)b);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Please provide names as command-line arguments\n");
        return 1;
    }

    qsort(argv + 1, argc - 1, sizeof(char *), compare);

    for (int i = 1; i < argc; ++i) {
        printf("hello, %s\n", argv[i]);
    }
    return 0;
}

This program accepts multiple names as arguments, sorts them alphabetically, and prints each with a greeting. The comparator function dereferences the pointers passed by qsort to compare actual string values, not their addresses.

String swapping behavior differs significantly depending on whether strings are stored in modifiable arrays or read-only string literals. Consider two variations:

// Case 1: Modifiable character arrays
char s1[80] = "Learning makes me happy";
char s2[80] = "Learning makes me sleepy";
char tmp[80];
strcpy(tmp, s1);
strcpy(s1, s2);
strcpy(s2, tmp); // Contents are successfully swapped
// Case 2: Pointers to string literals
char *s1 = "Learning makes me happy";
char *s2 = "Learning makes me sleepy";
char *tmp = s1;
s1 = s2;
s2 = tmp; // Only pointer values are swapped; original strings unchanged

In the first case, the strings are stored in writable memory, allowing strcpy to modify their contents. In the second, the pointers point to read-only memory; swapping the pointers merely changes which literal each points to, leaving the original strings untouched.

For string manipulation, functions like str_trunc demonstrate pointer arithmetic to modify strings in-place:

char *str_trunc(char *str, char x) {
    char *p = str;
    while (*p != '\0') {
        if (*p == x) {
            *p = '\0';
            break;
        }
        p++;
    }
    return str;
}

This function locates the first occurrence of a specified character and replaces it with a null terminator, effectively truncating the string from that point onward. The original string is modified directly, and the same pointer is returned for convenience.

Similarly, a function to replace characters through out a string uses pointer incrementation:

void replace(char *str, char old_char, char new_char) {
    while (*str) {
        if (*str == old_char)
            *str = new_char;
        str++;
    }
}

This version iterates through each byte until the null terminator, replacing matches without requiring index-based access.

For validation tasks such as checking Chinese ID numbers, strict length and character constraints are enforced:

int check_id(char *str) {
    int len = strlen(str);
    if (len != 18) return 0;

    for (int i = 0; i < 17; i++) {
        if (str[i] < '0' || str[i] > '9') return 0;
    }

    char last = str[17];
    if (!(last >= '0' && last <= '9') && last != 'X') return 0;
    return 1;
}

The function ensures exactly 18 characters, with the first 17 being digits and the last being either a digit or uppercase 'X'.

Text encoding and decoding can be implemented using modular arithmetic on alphabetic characters:

void encoder(char *str, int n) {
    int len = strlen(str);
    for (int i = 0; i < len; i++) {
        if (isalpha(str[i])) {
            if (islower(str[i])) {
                str[i] = (str[i] - 'a' + n) % 26 + 'a';
            } else {
                str[i] = (str[i] - 'A' + n) % 26 + 'A';
            }
        }
    }
}

void decoder(char *str, int n) {
    int len = strlen(str);
    for (int i = 0; i < len; i++) {
        if (isalpha(str[i])) {
            if (islower(str[i])) {
                str[i] = (str[i] - 'a' - n + 26) % 26 + 'a';
            } else {
                str[i] = (str[i] - 'A' - n + 26) % 26 + 'A';
            }
        }
    }
}

The encoder shifts each letter forward by n positions in the alphabet, wrapping around using modulo. The decoder reverses this by shifting backward, with +26 ensuring the result remains positive before applying modulo.

When working with two-dimensional arrays, multiple pointer-based access methods exist:

int x[2][4] = {{1, 9, 8, 4}, {2, 0, 4, 9}};
int *ptr1;
int (*ptr2)[4];

// Access via element pointer
ptr1 = &x[0][0];
for (int i = 0; i < 8; ++i) {
    printf("%d ", *(ptr1 + i));
}

// Access via array-of-ints pointer
ptr2 = x;
for (int row = 0; row < 2; ++row) {
    for (int col = 0; col < 4; ++col) {
        printf("%d ", *(*(ptr2 + row) + col));
    }
}

Here, ptr1 treats the array as a flat sequence of integers, while ptr2 maintains the 2D structure by pointing to entire rows, enabling natural row-wise traversal.

Tags: C string-manipulation pointer-arithmetic qsort command-line-arguments

Posted on Sun, 14 Jun 2026 18:13:32 +0000 by steveangelis