String Manipulation
<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.
#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.
#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
#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.
#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;
}'\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.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/tolowerfrom<ctype.h>convert one character at a time -- cast the argument tounsigned charfirstWhen building a string manually, always check remaining buffer space (including room for
\0) before every writePrefer bounded functions like
snprintfover manual concatenation chains when possible