How to Reverse a String in C++: 4 Practical Methods
Learn four ways to reverse a C++ string: std::reverse, reverse iterators, manual swaps, and C++20 ranges, with input handling, tests, and Unicode cautions.
Quick answer
The simplest way to reverse a string in C++ is to use std::reverse from <algorithm>:
#include <algorithm>
#include <string>
std::string text = "stressed";
std::reverse(text.begin(), text.end());
// text is now "desserts"
This changes the existing string in place. It runs in O(n) time and uses O(1) extra workspace apart from the string itself. The second iterator is the past-the-end iterator, so the range is [first, last): pass end(), not the index of the final character. The algorithm returns void, so read the changed string after the call.
Reversing a string is a small operation, but the right implementation depends on what you need to preserve. Do you want to modify a buffer, return a second value, read a complete line including spaces, or avoid the standard algorithm while practicing the underlying logic? C++ supports all four cases cleanly.
This guide focuses on ordinary ASCII examples such as commands and English text. That limitation matters: a std::string is a sequence of char elements, not automatically a sequence of human-perceived characters. Unicode details appear later so that a short demo does not quietly become incorrect production code.
1. Reverse in place with std::reverse
For a mutable std::string, the standard algorithm is usually the clearest choice:
#include <algorithm>
#include <string>
void reverse_in_place(std::string& text) {
std::reverse(text.begin(), text.end());
}
std::reverse swaps corresponding elements from the two ends until the iterators meet. Its target is the half-open range first, last), which includes the element at first but excludes last; this is why the natural call uses begin() and end(). See the [cppreference documentation for std::reverse for its range requirements and complexity.
The function returns void. It is not an expression that produces a reversed string, so the following assignment would not compile:
// Incorrect: std::reverse does not return a string.
// std::string result = std::reverse(text.begin(), text.end());
Instead, call it and use text. An empty string is already reversed, and a one-character string stays unchanged without any special case. The following statements can go inside a function after defining reverse_in_place above:
std::string a;
reverse_in_place(a); // ""
std::string b = "A";
reverse_in_place(b); // "A"
std::string c = "C++: fast?";
reverse_in_place(c); // "?tsaf :++C"
This method is a good default when the caller owns the string and does not need its original ordering afterward. It is also more expressive than manually swapping characters: the intent is immediately visible, and the standard library handles the iterator details.
Do not pass a const std::string to this function. Reversal swaps elements, so the range must be writable. Likewise, a std::string_view is a non-owning view exposing read-only characters; it is useful for inspecting text, not for this in-place operation. Make a std::string copy first if mutation is required.
2. Build a reversed copy with reverse iterators
Sometimes the original must remain unchanged. A std::string can be constructed from its reverse-iterator range:
#include <string>
std::string reversed_copy(const std::string& original) {
return std::string(original.rbegin(), original.rend());
}
For a non-empty string, rbegin() starts at the last character and advances toward the front; rend() marks the end of reverse iteration. The constructor reads that range in reverse order, producing a new string. The basic_string reference documents these reverse iterators and the string’s range-based construction.
For example, after defining the function above:
const std::string original = "drawer";
const std::string reversed = reversed_copy(original);
// original == "drawer", reversed == "reward"
This is a copy, not a view. It requires O(n) time and O(n) additional storage for the returned string, but it is the right trade-off when both versions are needed or when the input is const. Unlike std::reverse, it does not mutate the caller’s object.
A reverse-iterator constructor is different from a C++20 std::ranges::reverse_view, which can be created using the std::views::reverse adaptor. A constructor materializes a new std::string; a view presents an existing range in reverse iteration order without rearranging its elements. Choose the constructor when you need an independent string, and a view when a lazy traversal is enough. A reverse view is not inherently read-only: whether its elements can be changed depends on the underlying range.
3. Implement the two-pointer swap manually
Writing the algorithm yourself can help when learning, adapting the operation, or working in a setting where you want explicit control over each swap:
#include <string>
#include <utility>
void reverse_two_pointers(std::string& text) {
std::size_t left = 0;
std::size_t right = text.size();
while (left < right) {
--right;
if (left >= right) {
break;
}
std::swap(text[left], text[right]);
++left;
}
}
The important detail is that right starts at text.size(), which is one past the final valid index. The code decrements it before indexing. The loop tests left < right, so an empty string never decrements or indexes anything, and a one-character string stops after moving right to zero. The explicit second check prevents a middle character from being swapped with itself and keeps the unsigned index logic easy to audit.
An alternative uses an inclusive right index only after checking for emptiness, but mixing size_t with size() - 1 is a common source of underflow bugs. The half-open approach above avoids subtracting from zero before the loop begins.
Like std::reverse, this algorithm is O(n) time and O(1) extra space. It reverses elements exactly as the standard algorithm does; it does not add Unicode awareness. In normal application code, prefer the library call unless the manual steps are part of the lesson or a specialized algorithm requires them.
4. Use std::ranges::reverse in C++20
C++20’s ranges algorithms accept a range directly, so the call can be shorter:
#include <algorithm>
#include <string>
void reverse_cxx20(std::string& text) {
std::ranges::reverse(text);
}
Compile this version as C++20 or later with a standard library that supports the ranges algorithms. The C++20 std::ranges::reverse reference describes the range overload and its return value. For this lvalue string argument, it returns the past-the-end iterator. The ordinary std::reverse and ranges version both rearrange the elements in place and perform linear work rather than creating a second string.
Do not confuse std::ranges::reverse(text) with std::views::reverse(text). The former mutates a writable range. The latter creates a lazy reverse view for traversal or composing operations; creating that view does not reorder the original string. For a reversed copy, one straightforward option remains std::string(text.rbegin(), text.rend()).
For C++17 projects, use std::reverse; it is broadly available and does not require ranges support. Avoid non-standard helpers such as strrev or headers such as bits/stdc++.h: they reduce portability and hide the actual standard dependencies.
Reading the string correctly: getline versus operator>>
Reversal only works on the text you actually read. std::cin >> text skips leading whitespace and stops at the next whitespace, so input such as two words produces only two. To preserve spaces and punctuation through the newline, use std::getline. Here is a complete program:
#include <algorithm>
#include <iostream>
#include <string>
int main() {
std::string text;
if (!std::getline(std::cin, text)) {
return 1;
}
std::reverse(text.begin(), text.end());
std::cout << text << '\n';
}
Save it as reverse.cpp, compile with g++ -std=c++17 reverse.cpp -o reverse, and run it. Enter two words on one line; the output is sdrow owt. The other code blocks are functions or statements to place in your own program, not separate complete executables.
The cppreference std::getline reference explains that the default delimiter is the newline and that the delimiter is consumed but not stored. Thus a line containing hello! keeps both leading and trailing spaces, which may be significant when reversing it.
A common input bug occurs when formatted extraction comes first:
int count;
std::cin >> count;
std::string text;
std::getline(std::cin, text); // may read the leftover newline
After operator>>, the newline remains in the stream. If the next line is meant to be a full line and leading whitespace is not meaningful, std::getline(std::cin >> std::ws, text) is concise. But std::ws consumes leading whitespace, including blank lines, so it is wrong when those must survive.
For a “finish the current line, then read the next one” policy, discard through the newline explicitly. Include <limits> and use this pattern inside your input-handling function:
int count;
std::string text;
if (!(std::cin >> count)) {
return; // handle invalid numeric input
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
if (!std::getline(std::cin, text)) {
return; // handle missing line
}
The bare return statements assume a function returning void; adapt error handling to your application. This policy discards any remaining text on the numeric-input line, so it should only be used when the next line is the intended string.
Complexity and correctness checks
For an input of n elements, in-place reversal performs O(n) work and needs O(1) algorithm workspace. A reversed copy also takes O(n) time but uses O(n) storage for the new result. Both statements assume ordinary element operations are constant-time; they exclude memory already owned by the caller’s string.
Useful tests cover more than the familiar word hello. Check an empty string, one character, spaces, punctuation, a palindrome, and repeated characters. The double-reversal property is useful: reversing elements twice should restore the exact original sequence. That property alone does not prove the intermediate output is valid Unicode.
After defining the helper functions, a test function can include assertions like these with <cassert>:
std::string value = "A man, a plan!";
reverse_in_place(value);
assert(value == "!nalp a ,nam A");
reverse_in_place(value);
assert(value == "A man, a plan!");
const std::string source = "drawer";
assert(reversed_copy(source) == "reward");
assert(source == "drawer");
Also run each in-place method on "", "A", " hi! ", "level", and "aabb". Compare expected outputs, not just lengths. The copy method should leave the original unchanged; the in-place methods should change only the supplied string.
Important UTF-8 limitation
For UTF-8 text, std::string stores encoded bytes. Reversing those bytes can split a multibyte code point and produce invalid UTF-8. Changing the type to std::wstring or std::u32string does not automatically solve the full problem: code points are not necessarily user-perceived grapheme clusters. Combining marks or joined emoji sequences can still display incorrectly when their code points are reversed.
Therefore, these examples intentionally use ASCII. If an application must reverse user-visible Unicode text, use a Unicode-aware library to decode it, identify grapheme-cluster boundaries, and reverse complete clusters rather than raw bytes. The library and policy depend on the application’s Unicode requirements; there is no universal one-line standard-library fix.
For related language-specific patterns, compare these guides to reversing a string in Java and reversing a string in Python. The same high-level choice applies: define what a “character” means before handling non-ASCII text.
Practical pitfalls to avoid
Do not treat a character index as an iterator. text[text.size() - 1] is the final element only when the string is non-empty, while text.end() is always the correct one-past-the-end boundary for a full-range standard algorithm. The library call works for every string size because it receives a range, not a manually calculated final index.
Do not accidentally reverse only part of the value. A call such as std::reverse(text.begin(), text.begin() + 3) reverses only the first three elements, and requires at least three elements. That can be useful, but it is not a full-string reversal. If the requirement is “reverse everything,” make the range visibly span from begin() to end().
Remember that punctuation and whitespace are elements too. Reversing "red blue" produces "eulb der". That differs from reversing each word independently or changing word order. Decide which transformation you need before selecting an algorithm.
Keep ownership and lifetime clear. A reversed-copy function returns an owning std::string, so its result remains valid independently of the input. A view or pair of iterators refers to an underlying range; changing or destroying that range can invalidate access. For a value crossing an API boundary, an owning string is often the less surprising interface.
Which method should you choose?
Use std::reverse(text.begin(), text.end()) for an ordinary in-place operation. Use the reverse-iterator constructor when you need a separate copy. Use two pointers to learn or demonstrate the algorithm, and choose std::ranges::reverse when your project already supports C++20.
For a quick one-off transformation without writing a program, you can use the Text Reverser tool; choose the mode that matches whether you want characters or words reordered. In code, correct input boundaries, ownership, encoding, and mutation semantics matter more than a clever one-line trick. State those choices clearly, then test them with representative input.
Try the Text Reverser tool
Reverse text, words, or letters instantly — free and entirely in your browser.
Open the tool