CString Manipulation

String Manipulation

With the fundamentals of <string.h> in hand, this page works through a few practical, self-contained string manipulation examples: reversing a string in place, trimming surrounding whitespace, and converting case using <ctype.h>. Along the way, you will build strings manually with a buffer and a tracked index — a pattern that shows up constantly in real C code, and one where careful bounds tracking is essential.
Reversing a String In Place

Because a C string is just an array, you can reverse it without any extra memory by swapping characters from the two ends inward.

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

void reverse(char str[]) {
    int left = 0;
    int right = (int) strlen(str) - 1;

    while (left < right) {
        char temp = str[left];
        str[left] = str[right];
        str[right] = temp;
        left++;
        right--;
    }
}

int main(void) {
    char word[] = "hello";
    reverse(word);
    printf("%s\n", word); // olleh
    return 0;
}
Trimming Leading and Trailing Whitespace

Trimming combines a scan from the front, a scan from the back, and a shift — a good exercise in careful indexing.

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

void trim(char str[]) {
    int start = 0;
    while (isspace((unsigned char) str[start])) {
        start++;
    }

    int end = (int) strlen(str) - 1;
    while (end > start && isspace((unsigned char) str[end])) {
        end--;
    }

    int newLength = end - start + 1;
    for (int i = 0; i < newLength; i++) {
        str[i] = str[start + i];
    }
    str[newLength] = '\0'; // re-terminate at the new, shorter length
}

int main(void) {
    char text[] = "   hello world   ";
    trim(text);
    printf("[%s]\n", text); // [hello world]
    return 0;
}
Converting Case with ctype.h

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

void toUpperCase(char str[]) {
    for (int i = 0; str[i] != '\0'; i++) {
        str[i] = (char) toupper((unsigned char) str[i]);
    }
}

int main(void) {
    char name[] = "Ada Lovelace";
    toUpperCase(name);
    printf("%s\n", name); // ADA LOVELACE
    return 0;
}
Building a String Manually with a Buffer

Sometimes you need to construct a string piece by piece rather than using a single library call. The safe way to do this is to always track how many bytes you have written and how many the buffer has left, checking before every write.

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

int main(void) {
    char buffer[20];
    int index = 0;

    const char *parts[] = {"Hello", ", ", "world", "!"};
    int numParts = 4;

    for (int p = 0; p < numParts; p++) {
        int len = (int) strlen(parts[p]);

        // Only copy if there's room left for len chars PLUS the terminator
        if (index + len >= (int) sizeof(buffer)) {
            break; // stop before overflowing -- do not silently corrupt memory
        }

        strcpy(buffer + index, parts[p]);
        index += len;
    }
    buffer[index] = '\0'; // ensure termination even if the loop broke early

    printf("%s\n", buffer); // Hello, world!
    return 0;
}
Manual string building is a prime spot for buffer overflows
Every time you write into a buffer at a computed offset, you must know exactly how many bytes are left before you write — including room for the final null terminator. It is easy to forget the +1 for '\0', or to accumulate an index without re-checking it against the buffer size on every iteration. When building strings manually, always compare currentIndex + amountToAdd against the buffer's total size before writing, not after.
Prefer snprintf for formatted building
For building formatted strings, snprintf(buf, sizeof(buf), "...", ...) is generally safer than manual strcat/strcpy chains, because it always respects the given buffer size and never writes past it.
  • Reversing a string in place swaps characters from both ends inward using two indices

  • Trimming whitespace requires careful index bookkeeping: find the real start and end, then shift and re-terminate

  • toupper/tolower from <ctype.h> convert one character at a time -- cast the argument to unsigned char first

  • When building a string manually, always check remaining buffer space (including room for \0) before every write

  • Prefer bounded functions like snprintf over manual concatenation chains when possible