Reversing a string is one of the most fundamental operations in C programming, serving as a classic introduction to pointer arithmetic, array manipulation, and memory management. And unlike higher-level languages that offer built-in methods like reverse() or slicing syntax, C requires the developer to understand exactly how data sits in memory. Writing a C program to reverse a string forces you to confront the null-terminator, the difference between string literals and mutable arrays, and the efficiency of in-place algorithms versus auxiliary memory approaches The details matter here..
This guide explores every practical method to achieve this, from the standard library shortcut to the manual implementations expected in technical interviews and embedded systems development.
Understanding Strings in C Memory
Before writing code, it is critical to visualize what a string actually is in C. Still, this null terminator is not optional; it marks the logical end of the string. And a string is a contiguous block of memory of type char, terminated by a null character (\0). Functions like printf("%s", str) and strlen(str) rely entirely on this byte to know where to stop reading.
Easier said than done, but still worth knowing.
The moment you declare char str[] = "HELLO";, the compiler allocates 6 bytes: H, E, L, L, O, \0. Which means the variable str decays to a pointer to the first element (&str[0]). Reversing this string means swapping the first character with the last (ignoring the null terminator), the second with the second-to-last, and so on, until the pointers meet in the middle.
A crucial distinction exists between string literals and character arrays.
char *ptr = "HELLO";creates a pointer to a read-only memory segment (often.rodata). Attempting to modifyptr[0] = 'J';results in Undefined Behavior (typically a segmentation fault). Which means *char arr[] = "HELLO";creates a mutable array on the stack. Modifyingarr[0]is perfectly safe.
Any C program to reverse a string that modifies the buffer must operate on a mutable array, not a string literal pointer.
Method 1: The Standard Library Approach (strrev)
Many beginners search for a built-in reverse() function. Still, in the C Standard Library (ISO C99/C11), no such standard function exists. Even so, legacy compilers (like Turbo C++) and some modern Windows-specific headers (<string.h> in MSVC) provide strrev() Most people skip this — try not to..
#include
#include // Non-standard on Linux/GCC
int main() {
char str[] = "PROGRAMMING";
printf("Original: %s\n", str);
// Warning: strrev is not POSIX/ISO standard
strrev(str);
printf("Reversed: %s\n", str);
return 0;
}
Verdict: Avoid strrev for portable code. It will fail to compile on GCC/Clang (Linux/macOS) without custom implementation. It is useful only for quick throwaway programs on Windows-specific toolchains Most people skip this — try not to. That alone is useful..
Method 2: The Classic In-Place Reversal (Two Pointers)
We're talking about the gold standard for a C program to reverse a string. Because of that, it uses O(1) auxiliary space and O(N) time complexity. It modifies the original buffer directly, making it memory efficient—critical for embedded systems.
Algorithm Logic
- Initialize two pointers:
leftat index 0 (start) andrightatstrlen(str) - 1(end, before null terminator). - While
left < right:- Swap
str[left]andstr[right]. - Increment
left. - Decrement
right.
- Swap
- Stop when pointers cross or meet.
Implementation
#include
#include
void reverseString(char *str) {
// Safety check: Handle NULL or empty string
if (str == NULL) return;
int left = 0;
int right = strlen(str) - 1; // Index of last valid char
while (left < right) {
// Swap using a temporary variable
char temp = str[left];
str[left] = str[right];
str[right] = temp;
left++;
right--;
}
}
Short version: it depends. Long version — keep reading.
int main() {
// MUST be an array, not a pointer to literal
char text[] = "ALGORITHM";
printf("Before: %s\n", text);
reverseString(text);
printf("After: %s\n", text);
return 0;
}
Why this works
The loop condition left < right handles both even and odd lengths perfectly It's one of those things that adds up..
- Even length (e.g., 4 chars): Indices 0,1,2,3. Swaps (0,3) then (1,2).
leftbecomes 2,rightbecomes 1. Loop exits. - Odd length (e.g., 5 chars): Indices 0,1,2,3,4. Swaps (0,4) then (1,3).
leftbecomes 2,rightbecomes 2. Loop exits. The middle character (index 2) stays put, which is correct.
Method 3: Pointer Arithmetic Version
In C, array indexing str[i] is syntactic sugar for *(str + i). Professional C developers often prefer pointer arithmetic for clarity and potential compiler optimization. This version eliminates the index variables entirely Surprisingly effective..
#include
#include
void reversePointerStyle(char *str) {
if (!str) return;
char *start = str;
char *end = str + strlen(str) - 1; // Point to last char
char temp;
while (start < end) {
temp = *start;
*start = *end;
*end = temp;
start++; // Move pointer to next address
end--; // Move pointer to previous address
}
}
This compiles to nearly identical machine code as the index version but signals a deeper understanding of C's memory model. The condition start < end compares memory addresses directly.
Method 4: Recursive Reversal (Academic Interest)
Recursion is rarely used for string reversal in production C code due to stack overflow risk on long strings and O(N) stack space complexity. That said, it is a favorite interview question to test understanding of the call stack.
#include
#include
// Helper to swap chars at two indices
void swapChars(char *a, char *b) {
char temp = *a;
*a = *b;
*b = temp;
}
void reverseRecursive(char *str, int left, int right) {
// Base case: pointers met or crossed
if (left >= right) return;
swapChars(&str[left], &str[right]);
// Recursive step: move inward
reverseRecursive(str, left + 1, right - 1);
}
void reverseStringRecursive(char *str) {
if (!str) return;
reverseRecursive(str, 0, strlen(str) - 1);
}
Trace for "CAT" (len 3):
- Call
rev(0, 2): SwapCandT-> "TAC". Callrev(1, 1). - Call
rev(1, 1): Base case hit (1 >= 1). Return. - Stack unwinds. Result: "TAC".
Warning: On a typical 8MB stack, a string
The warning about stack exhaustion is worth elaborating. For a string of length N the algorithm makes roughly N/2 calls, so a million‑character string would need about five hundred thousand frames. In a typical C environment the default stack size is on the order of a few megabytes. Each recursive invocation consumes a stack frame that holds the return address, the parameters (left and right), and any local variables. On the flip side, even if each frame occupies only 32 bytes, the total demand climbs to 16 MB, far beyond the usual stack limit and guaranteeing a crash. So naturally, while the recursive version is elegant for teaching purposes, it is unsafe for production code unless the input size is tightly bounded.
Because the iterative approaches manipulate the same memory buffer in place, they achieve O(N) time with O(1) auxiliary space, making them the pragmatic choice for real‑world applications. The index‑based loop and the pointer‑arithmetic version are essentially equivalent in performance; the latter may be marginally faster on some compilers because it avoids the bounds‑checking that the compiler sometimes inserts for array subscripting. Both are straightforward to read, easy to debug, and safe for strings of any length Worth knowing..
When portability matters, it is advisable to guard the function against a null pointer and to avoid reliance on non‑standard library routines such as strrev, which are not part of the ISO C standard. A minimal, production‑ready implementation therefore looks like this:
#include
#include
void reverseString(char *text) {
if (text == NULL) return; // Guard against invalid input
int left = 0;
int right = (int)strlen(text) - 1;
while (left < right) {
char tmp = text[left];
text[left] = text[right];
text[right] = tmp;
++left;
--right;
}
}
Beyond the mechanics of swapping characters, a few best‑practice considerations deserve mention:
-
Const‑correctness – If the function does not need to modify the original buffer, declare the parameter as
const char *and work on a temporary copy. In scenarios where in‑place modification is required, the non‑const version is appropriate That alone is useful.. -
Boundary checks – The loop condition
left < rightautomatically handles both even and odd lengths, eliminating off‑by‑one errors. No additional checks are needed after the loop terminates Surprisingly effective.. -
Performance profiling – For extremely performance‑critical code, hand‑optimised assembly or SIMD intrinsics can reverse a buffer in fewer cycles, but the gain is usually marginal compared with the clarity loss Not complicated — just consistent..
-
Thread safety – Since the function operates on a mutable buffer, concurrent calls on the same memory region must be synchronized externally; the algorithm itself is re‑entrant No workaround needed..
Summarising the four techniques presented:
- Index‑based iteration offers the clearest balance of readability, safety, and efficiency.
- Pointer‑arithmetic iteration conveys a low‑level C mindset while delivering the same asymptotic performance.
- Recursive reversal is instructive for understanding recursion and stack dynamics but is unsuitable for large inputs.
- Standard library functions (e.g.,
strrevon GNU extensions) may be convenient but sacrifice portability.
In practice, the index‑based method is the de‑facto standard for reversing strings in C. It is simple, dependable, and works uniformly across all compilers and platforms. By adhering to this approach, developers can avoid the pitfalls of deep recursion, ensure predictable resource usage, and maintain code that is easy to maintain and audit.
Conclusion
String reversal in C is fundamentally an O(N) operation that can be performed safely in place with constant extra space. While academic curiosity may lead one to explore recursion, the iterative solutions — whether written with explicit indices or pointer arithmetic — provide the optimal blend of performance, reliability, and readability. For any production codebase, the index‑based loop remains the recommended implementation, delivering correct results for strings of any length without risking stack overflow or sacrificing clarity The details matter here..