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

", 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

", 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: