How do you reverse a string in place in C or C++?
The standard algorithm is to use pointers to the start / end, and walk them inward until they meet or cross in the middle. Swap as you go.
Reverse ASCII string, i.e. a 0-terminated array where every character fits in 1 char
. (Or other non-multibyte character sets).
void strrev(char *head){ if (!head) return; char *tail = head; while(*tail) ++tail; // find the 0 terminator, like head+strlen --tail; // tail points to the last real char // head still points to the first for( ; head < tail; ++head, --tail) { // walk pointers inwards until they meet or cross in the middle char h = *head, t = *tail; *head = t; // swapping as we go *tail = h; }}
// test program that reverses its args#include <stdio.h>int main(int argc, char **argv){ do { printf("%s ", argv[argc-1]); strrev(argv[argc-1]); printf("%s\n", argv[argc-1]); } while(--argc); return 0;}
The same algorithm works for integer arrays with known length, just use tail = start + length - 1
instead of the end-finding loop.
(Editor's note: this answer originally used XOR-swap for this simple version, too. Fixed for the benefit of future readers of this popular question. XOR-swap is highly not recommended; hard to read and making your code compile less efficiently. You can see on the Godbolt compiler explorer how much more complicated the asm loop body is when xor-swap is compiled for x86-64 with gcc -O3.)
Ok, fine, let's fix the UTF-8 chars...
(This is XOR-swap thing. Take care to note that you must avoid swapping with self, because if *p
and *q
are the same location you'll zero it with a^a==0. XOR-swap depends on having two distinct locations, using them each as temporary storage.)
Editor's note: you can replace SWP with a safe inline function using a tmp variable.
#include <bits/types.h>#include <stdio.h>#define SWP(x,y) (x^=y, y^=x, x^=y)void strrev(char *p){ char *q = p; while(q && *q) ++q; /* find eos */ for(--q; p < q; ++p, --q) SWP(*p, *q);}void strrev_utf8(char *p){ char *q = p; strrev(p); /* call base case */ /* Ok, now fix bass-ackwards UTF chars. */ while(q && *q) ++q; /* find eos */ while(p < --q) switch( (*q & 0xF0) >> 4 ) { case 0xF: /* U+010000-U+10FFFF: four bytes. */ SWP(*(q-0), *(q-3)); SWP(*(q-1), *(q-2)); q -= 3; break; case 0xE: /* U+000800-U+00FFFF: three bytes. */ SWP(*(q-0), *(q-2)); q -= 2; break; case 0xC: /* fall-through */ case 0xD: /* U+000080-U+0007FF: two bytes. */ SWP(*(q-0), *(q-1)); q--; break; }}int main(int argc, char **argv){ do { printf("%s ", argv[argc-1]); strrev_utf8(argv[argc-1]); printf("%s\n", argv[argc-1]); } while(--argc); return 0;}
- Why, yes, if the input is borked, this will cheerfully swap outside the place.
- Useful link when vandalising in the UNICODE: http://www.macchiato.com/unicode/chart/
- Also, UTF-8 over 0x10000 is untested (as I don't seem to have any font for it, nor the patience to use a hexeditor)
Examples:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●░▒▓○◔◑◕● ●◕◑◔○▓▒░Räksmörgås sågrömskäR./strrev verrts/.