How to Reverse a String in Place in C or C++

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/.

Reversing a string in place in C - pointers?

The if (str) test protects you from dereferencing a null pointer and crashing.

The definition char *end = str; defines the variable end, a character pointer, and initializes it with the value stored in str (which is the address of the first character of the string that str points to).

The rest of the code determines the length of the string, and then arranges to swap pairs of characters from the two ends, working towards the middle of the string. Technically, the original code is not safe if it is passed an empty string (a pointer that points to the null byte at the end of a string). That's because it will decrement end to one byte before the byte that str points at. However, there is no guarantee that the address one byte before the start of a string is valid. The string might point to the first byte of a page of memory, and the prior page has never been mapped, leading to crashes or other problems.

It would be better to use strlen() to determine the length of the string.

void reverse(char *str)
{
if (str != 0 && *str != '\0') // Non-null pointer; non-empty string
{
char *end = str + strlen(str) - 1;

while (str < end)
{
char tmp = *str;
*str++ = *end;
*end-- = tmp;
}
}
}

Fastest way to reverse a string in C

Maybe something like this?

char *str_reverse_in_place(char *str, int len)
{
char *p1 = str;
char *p2 = str + len - 1;

while (p1 < p2) {
char tmp = *p1;
*p1++ = *p2;
*p2-- = tmp;
}

return str;
}

In-Place String Reverse in C

The problem is that you're trying to reverse a constant literal string, which is read only. Change the declaration of a in main to char a[] = "12"; to make it a writable char array instead

C - String Reversal Function with Pointers Not Running

the function requires modifiable char array. It cannot be called when parameter is a pointer to thestring literal.

Some additional remarks.

  1. This kind of function should return the reversed string. It is possible to use it as a parameter of another functions.
  2. strlen returns size_t not int. Use the correct type in the loops as well
#include <string.h>
#include <stdio.h>

char *reverse(char *str)
{
char *end = str + strlen(str) - !!*str;
char *wrk = str;

while(end > wrk)
{
char tmp = *wrk;
*wrk++ = *end;
*end-- = tmp;
}
return str;
}

int main(void)

{
char str[] = "0123456789";

printf("%s\n", reverse(str));
}

reversing a string in c with pointer

reversed will point to the middle of the string after a successful reversal. So you will print only what's from that position in the string onward (in this case " world"). You can verify this by printing string as well, and see that it was indeed reversed.

Return the original pointer chararray instead.

Reversing a string in C

If you want to practice advanced features of C, how about pointers?
We can toss in macros and xor-swap for fun too!

#include <string.h> // for strlen()

// reverse the given null-terminated string in place
void inplace_reverse(char * str)
{
if (str)
{
char * end = str + strlen(str) - 1;

// swap the values in the two given variables
// XXX: fails when a and b refer to same memory location
# define XOR_SWAP(a,b) do\
{\
a ^= b;\
b ^= a;\
a ^= b;\
} while (0)

// walk inwards from both ends of the string,
// swapping until we get to the middle
while (str < end)
{
XOR_SWAP(*str, *end);
str++;
end--;
}
# undef XOR_SWAP
}
}

A pointer (e.g. char *, read from right-to-left as a pointer to a char) is a data type in C that is used
to refer to location in memory of another value. In this case,
the location where a char is stored. We can dereference
pointers by prefixing them with an *, which gives us the value
stored at that location. So the value stored at str is *str.

We can do simple arithmetic with pointers. When we increment (or decrement)
a pointer, we simply move it to refer to the next (or previous)
memory location for that type of value. Incrementing pointers of
different types may move the pointer by a different number of
bytes because different values have different byte sizes in C.

Here, we use one pointer to refer to the first unprocessed
char of the string (str) and another to refer to the last (end).
We swap their values (*str and *end), and move the pointers
inwards to the middle of the string. Once str >= end, either
they both point to the same char, which means our original string had an
odd length (and the middle char doesn't need to be reversed), or
we've processed everything.

To do the swapping, I've defined a macro. Macros are text substitution
done by the C preprocessor. They are very different from functions,
and it's important to know the difference. When you call a function,
the function operates on a copy of the values you give it. When you call
a macro, it simply does a textual substitution - so the arguments you give
it are used directly.

Since I only used the XOR_SWAP macro once, it was probably overkill to define it,
but it made more clear what I was doing. After the C preprocessor expands the macro,
the while loop looks like this:

    while (str < end)
{
do { *str ^= *end; *end ^= *str; *str ^= *end; } while (0);
str++;
end--;
}

Note that the macro arguments show up once for each time they're used in the
macro definition. This can be very useful - but can also break your code
if used incorrectly. For example, if I had compressed the increment/decrement
instructions and the macro call into a single line, like

      XOR_SWAP(*str++, *end--);

Then this would expand to

      do { *str++ ^= *end--; *end-- ^= *str++; *str++ ^= *end--; } while (0);

Which has triple the increment/decrement operations, and doesn't actually
do the swap it's supposed to do.

While we're on the subject, you should know what xor (^) means. It's a basic
arithmetic operation - like addition, subtraction, multiplication, division, except
it's not usually taught in elementary school. It combines two integers bit by bit
- like addition, but we don't care about the carry-overs. 1^1 = 0, 1^0 = 1,
0^1 = 1, 0^0 = 0.

A well known trick is to use xor to swap two values. This works because of three basic
properties of xor: x ^ 0 = x, x ^ x = 0 and x ^ y = y ^ x for all values x and y. So say we have two
variables a and b that are initially storing two values
va and vb.


// initially:
// a == va
// b == vb
a ^= b;
// now: a == va ^ vb
b ^= a;
// now: b == vb ^ (va ^ vb)
// == va ^ (vb ^ vb)
// == va ^ 0
// == va
a ^= b;
// now: a == (va ^ vb) ^ va
// == (va ^ va) ^ vb
// == 0 ^ vb
// == vb

So the values are swapped. This does have one bug - when a and b are the same variable:


// initially:
// a == va
a ^= a;
// now: a == va ^ va
// == 0
a ^= a;
// now: a == 0 ^ 0
// == 0
a ^= a;
// now: a == 0 ^ 0
// == 0

Since we str < end, this never happens in the above code, so we're okay.

While we're concerned about correctness we should check our edge cases. The if (str) line should make sure we weren't given a NULL pointer for string. What about the empty string ""? Well strlen("") == 0, so we'll initialize end as str - 1, which means that the while (str < end) condition is never true, so we don't do anything. Which is correct.

There's a bunch of C to explore. Have fun with it!

Update: mmw brings up a good point, which is you do have to be slightly careful how you invoke this, as it does operate in-place.

 char stack_string[] = "This string is copied onto the stack.";
inplace_reverse(stack_string);

This works fine, since stack_string is an array, whose contents are initialized to the given string constant. However

 char * string_literal = "This string is part of the executable.";
inplace_reverse(string_literal);

Will cause your code to flame and die at runtime. That's because string_literal merely points to the string that is stored as part of your executable - which is normally memory that you are not allowed to edit by the OS. In a happier world, your compiler would know this, and cough an error when you tried to compile, telling you that string_literal needs to be of type char const * since you can't modify the contents. However, this is not the world my compiler lives in.

There are some hacks you could try to make sure that some memory is on the stack or in the heap (and is therefore editable), but they're not necessarily portable, and it could be pretty ugly. However, I'm more than happy to throw responsibility for this to the function invoker. I've told them that this function does in place memory manipulation, it's their responsibility to give me an argument that allows that.



Related Topics



Leave a reply



Submit