How do you reverse a string in place in C or C++?
Asked Answered
M

21

197

How do you reverse a string in C or C++ without requiring a separate buffer to hold the reversed string?

Microcopy answered 13/10, 2008 at 16:36 Comment(0)
P
134

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/.
Pairs answered 13/10, 2008 at 16:58 Comment(19)
Why not "*p ^= *q, *q ^= *p, *p ^= *q"?Flophouse
I'd say that if you are going to ask for "In place" without being more specific, it HAS to be the xor thing. Anything else isn't in-place. That said, this has no business being in production code anywhere ever. if you're ever even tempted to use it, quit engineering now.Meadowsweet
You think "in-place" means "no extra memory", not even O(1) memory for temporaries? What about the space on the stack for str and the return address?Flophouse
@Bill, that's not what the common definition of “in-place” means. In-place algorithms may use additional memory. However, the amount of this additional memory must not depend on the input – i.e. it must be constant. Therefore, swapping of values using additional storage is completely in-place.Berlinda
Oh no, Räksmörgås! Now I have to go to the fridge and make me one. :PNissy
Perhaps you'd like to check this question I've asked specifically about how to handle this task with UTF8 strings (not an universal solution, though). #199760Sorites
XOR swapping is slower than swapping via register on modern out-of-order processors.Augmented
I googled "in place string reversal" specifically to find an implementation of the XOR trick. Haters gonna hate, but I found what I was looking for so +1 from me.Haft
I ran some tests, and it seems that the "while (q && *q)" test should be changed. It should be "while (*q)" or changed to a separate "if (!q) { return; }" (if the intention was to catch NULL pointers). The problem is in the NULL case, the next statement, for(--q; p < q; ++p, --q), causes q to roll over to 0xFFFFFFFFFFFFFFFF (at least on the 64-bit CentOS machine I was testing on), which will cause the for loop to execute (since 0 is less than 0xFFFFFFFFFFFFFFFF, and the first "*p = *p ^ *q;" will then cause a de-reference of a null pointer, and a de-reference of 0xFFFFFFFFFFFFFFFF...Rico
Guys, most of you are wrong about definition of in-place. It has a strong definition: in-place algorithm uses O(logn) memory. Fox example do you consider quicksort in-place? It is in place and it uses logarithmic memory for storing data in recursion stack.Tara
@Tara You are wrong. In-place means one thing, and one thing only: constant memory, not O(logn). And you got it: quicksort isn’t in-place. Whoever claims that is wrong. In fact, many (ostensibly “in-place”) implementations of quicksort even use O(n) additional memory in the worst case (since they don’t guarantee logarithmic recursion depth. But like I said, quicksort isn’t really in-place to begin with.Berlinda
@KonradRudolph, yeah, I got it. Perhaps some time ago I read this article en.wikipedia.org/wiki/In-place_algorithm (section "In computational complexity") and forgot that this log n was applied there in a little bit different context.Tara
@AndersEurenius can you clarify on why do use use while(q && *q) ++q; to find the eos? Isn't it the same as while(*q) ++q;, since q is never going to be 0? How can you be sure that q or *q are going to be 0?Knitted
"you must avoid swapping with self, because a^a==0" - wrong. a ^ a == 0, but that's not a problem, because then you will do a ^ (a ^ a) which is a ^ 0 which is a. So the XOR swap works even if the two swappees (is there such a word) are equal.Felicafelicdad
char *q = p; while(q && *q) ++q; for(--q; p < q; ++p, --q) to do NULL detection is problematic. For the code progress to --q when q is NULL and at best that is the largest legal pointer, at worst it may be UB. Then the for() loop would iterate for a long time. Suggest a simple if (q != NULL) { do the rest of code }.Dorweiler
As an interviewer, I dock points for use of the old xor-swap trick. It's less efficient on modern processors and anybody who uses it because it's "neat" is not a great programmer -- "good", possibly, but not "great".Uncaused
Wow, every time I think I can't find an uglier code than last time, I find an uglier one.Caundra
@ChrisConway Yes and as a participant and winner also I can say that it's not going to confuse many people. It might have its uses there but it's not confusing either. I can think of numerous ways it might be useful in an entry but overall it's so unoriginal that it would be not relevant. They want much more balanced entries. Still there are times outside the contest where I might consider it though that's extremely limited too - and not in regular code.Dilley
As for while(q && *q) ++q; why not just use strchr(q, '\0'); ? Probably doesn't matter but it seems more natural. Of course there are other ways too. Maybe you just didn't want to include string.h even.Dilley
G
494
#include <algorithm>
std::reverse(str.begin(), str.end());

This is the simplest way in C++.

Gaut answered 13/10, 2008 at 16:39 Comment(4)
In C++ a string is represented by the string class. He didn't asked for "char star" or a "char brackets". Stay classy, C.Odlo
@fredsbend, the "ridiculously long" version of the selected answer handles a case which this simple answer doesn't - UTF-8 input. It shows the importance of fully specifying the problem. Besides the question was about code that would work in C as well.Electrical
This answer does handle the case if you use a UTF-8 aware string class (or possibly a utf-8 character class with std::basic_string). Besides, the question said "C or C++", not "C and C++". C++ only is "C or C++".Osmometer
This will completely break UTF-8 multibyte strings and in this day and age a total non-starter.Chiarra
A
169

Read Kernighan and Ritchie

#include <string.h>

void reverse(char s[])
{
    int length = strlen(s) ;
    int c, i, j;

    for (i = 0, j = length - 1; i < j; i++, j--)
    {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}
Anstice answered 14/10, 2008 at 3:9 Comment(13)
Tested on my iphone this is slower than using raw pointer addresses by about 15%Tko
Shouldn't variable "c" be a char instead of an int?Blooded
@Blooded - In C, whenever a character constant or variable is used in an expression in C, it is automatically converted & treated as an integer. If you have a linux termminal, you can see the ascii codes by typing man asciiSoso
Its important to note in this example that the string s must be declared in an array form. In other words, char s[] = "this is ok" rather than char *s="cannot do this" because the latter results in a string constant which cannot be modifiedSoso
length, i, j should be size_t, but then would have trouble when length == 0.Dorweiler
@PsychoDad Did you enable optimizations when you did the iPhone benchmark of array access vs pointer??Foah
@brandin Hopefully I did it in release mode, but I don't remember.Tko
With apologizes to "The Godfather" .... "Leave the guns, bring the K&R". As a C bigot, I'd use pointers, as they're simpler and more straight-forward for this problem, but the code would be less portable to C#, Java, etc.Cholecystectomy
I don't understand why this isn't the accepted answer. It's the simplest, most robust solution that performs in O(1) space. It also performs in O(log(n)) time, which is awesome.Oscillation
@Eric This does not run in O(log(n)) time. It runs in O(n) if you're referring to the number of character swaps the code performs, for n of string length, then n swaps are performed. If you we're talking about the amount of loops performed then its still O(n) - albeit O(n/2) but you drop the constants in Big O notation.Nmr
@Eric In addition to what Stephen says it - as user1527227 also says - requires it to be in array form. That would be another good reason it's not accepted.Dilley
@Tko Then you have a bad compilerEvars
Sorry, but this solution is not "in place". When I used to teach C programming, one of the problems I always gave my students was to reverse the string in place using only two registers and no stack variables to hold any of the string characters. The trick was using exclusive or. It was easier back then though because we could easily write past the \0 and do a buffer overrun. This was allowed as long as they only overwrote the \0, giving them a byte to play with.Hesperus
P
134

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/.
Pairs answered 13/10, 2008 at 16:58 Comment(19)
Why not "*p ^= *q, *q ^= *p, *p ^= *q"?Flophouse
I'd say that if you are going to ask for "In place" without being more specific, it HAS to be the xor thing. Anything else isn't in-place. That said, this has no business being in production code anywhere ever. if you're ever even tempted to use it, quit engineering now.Meadowsweet
You think "in-place" means "no extra memory", not even O(1) memory for temporaries? What about the space on the stack for str and the return address?Flophouse
@Bill, that's not what the common definition of “in-place” means. In-place algorithms may use additional memory. However, the amount of this additional memory must not depend on the input – i.e. it must be constant. Therefore, swapping of values using additional storage is completely in-place.Berlinda
Oh no, Räksmörgås! Now I have to go to the fridge and make me one. :PNissy
Perhaps you'd like to check this question I've asked specifically about how to handle this task with UTF8 strings (not an universal solution, though). #199760Sorites
XOR swapping is slower than swapping via register on modern out-of-order processors.Augmented
I googled "in place string reversal" specifically to find an implementation of the XOR trick. Haters gonna hate, but I found what I was looking for so +1 from me.Haft
I ran some tests, and it seems that the "while (q && *q)" test should be changed. It should be "while (*q)" or changed to a separate "if (!q) { return; }" (if the intention was to catch NULL pointers). The problem is in the NULL case, the next statement, for(--q; p < q; ++p, --q), causes q to roll over to 0xFFFFFFFFFFFFFFFF (at least on the 64-bit CentOS machine I was testing on), which will cause the for loop to execute (since 0 is less than 0xFFFFFFFFFFFFFFFF, and the first "*p = *p ^ *q;" will then cause a de-reference of a null pointer, and a de-reference of 0xFFFFFFFFFFFFFFFF...Rico
Guys, most of you are wrong about definition of in-place. It has a strong definition: in-place algorithm uses O(logn) memory. Fox example do you consider quicksort in-place? It is in place and it uses logarithmic memory for storing data in recursion stack.Tara
@Tara You are wrong. In-place means one thing, and one thing only: constant memory, not O(logn). And you got it: quicksort isn’t in-place. Whoever claims that is wrong. In fact, many (ostensibly “in-place”) implementations of quicksort even use O(n) additional memory in the worst case (since they don’t guarantee logarithmic recursion depth. But like I said, quicksort isn’t really in-place to begin with.Berlinda
@KonradRudolph, yeah, I got it. Perhaps some time ago I read this article en.wikipedia.org/wiki/In-place_algorithm (section "In computational complexity") and forgot that this log n was applied there in a little bit different context.Tara
@AndersEurenius can you clarify on why do use use while(q && *q) ++q; to find the eos? Isn't it the same as while(*q) ++q;, since q is never going to be 0? How can you be sure that q or *q are going to be 0?Knitted
"you must avoid swapping with self, because a^a==0" - wrong. a ^ a == 0, but that's not a problem, because then you will do a ^ (a ^ a) which is a ^ 0 which is a. So the XOR swap works even if the two swappees (is there such a word) are equal.Felicafelicdad
char *q = p; while(q && *q) ++q; for(--q; p < q; ++p, --q) to do NULL detection is problematic. For the code progress to --q when q is NULL and at best that is the largest legal pointer, at worst it may be UB. Then the for() loop would iterate for a long time. Suggest a simple if (q != NULL) { do the rest of code }.Dorweiler
As an interviewer, I dock points for use of the old xor-swap trick. It's less efficient on modern processors and anybody who uses it because it's "neat" is not a great programmer -- "good", possibly, but not "great".Uncaused
Wow, every time I think I can't find an uglier code than last time, I find an uglier one.Caundra
@ChrisConway Yes and as a participant and winner also I can say that it's not going to confuse many people. It might have its uses there but it's not confusing either. I can think of numerous ways it might be useful in an entry but overall it's so unoriginal that it would be not relevant. They want much more balanced entries. Still there are times outside the contest where I might consider it though that's extremely limited too - and not in regular code.Dilley
As for while(q && *q) ++q; why not just use strchr(q, '\0'); ? Probably doesn't matter but it seems more natural. Of course there are other ways too. Maybe you just didn't want to include string.h even.Dilley
F
43

Non-evil C, assuming the common case where the string is a null-terminated char array:

#include <stddef.h>
#include <string.h>

/* PRE: str must be either NULL or a pointer to a 
 * (possibly empty) null-terminated string. */
void strrev(char *str) {
  char temp, *end_ptr;

  /* If str is NULL or empty, do nothing */
  if( str == NULL || !(*str) )
    return;

  end_ptr = str + strlen(str) - 1;

  /* Swap the chars */
  while( end_ptr > str ) {
    temp = *str;
    *str = *end_ptr;
    *end_ptr = temp;
    str++;
    end_ptr--;
  }
}
Flophouse answered 13/10, 2008 at 17:0 Comment(7)
Rather than using a while loop to find the end pointer, can't you use something like end_ptr = str + strlen (str); I know that will do practically the same thing, but I find it clearer.Camorra
Fair enough. I was trying (and failing) to avoid the off-by-one error in @uvote's answer.Flophouse
Aside from a potential performance improvement with maybe int temp, this solution looks best. +1Dorweiler
@chux-ReinstateMonica Yes. It's beautiful. Personally I would remove the ()s from the !(*str) though.Dilley
@PeterKühne Yes or strchr() searching for '\0'. I thought of both. Not need for a loop.Dilley
Why is this considered non-evil? What is an evil implementation and why is it important to distinguish between them? ThanksKimberykimble
This answer is 13 years old. You think I have any idea what I meant back then?! I think the top answer back then used XOR swap, which is non-obvious and buggy. "Evil" was probably not the right word to use.Flophouse
B
36

It's been a while and I don't remember which book taught me this algorithm, but I thought it was quite ingenious and simple to understand:

char input[] = "moc.wolfrevokcats";

int length = strlen(input);
int last_pos = length-1;
for(int i = 0; i < length/2; i++)
{
    char tmp = input[i];
    input[i] = input[last_pos - i];
    input[last_pos - i] = tmp;
}

printf("%s\n", input);

A visualization of this algorithm, courtesy of slashdottir:

Visualization of the algorithm to reverse a string in place

Beanery answered 3/7, 2011 at 0:16 Comment(1)
It's an interesting variation yes. Should technically be a size_t though.Dilley
M
23

Note that the beauty of std::reverse is that it works with char * strings and std::wstrings just as well as std::strings

void strrev(char *str)
{
    if (str == NULL)
        return;
    std::reverse(str, str + strlen(str));
}
Migraine answered 13/10, 2008 at 18:26 Comment(0)
S
12

In the interest of completeness, it should be pointed out that there are representations of strings on various platforms in which the number of bytes per character varies depending on the character. Old-school programmers would refer to this as DBCS (Double Byte Character Set). Modern programmers more commonly encounter this in UTF-8 (as well as UTF-16 and others). There are other such encodings as well.

In any of these variable-width encoding schemes, the simple algorithms posted here (evil, non-evil or otherwise) would not work correctly at all! In fact, they could even cause the string to become illegible or even an illegal string in that encoding scheme. See Juan Pablo Califano's answer for some good examples.

std::reverse() potentially would still work in this case, as long as your platform's implementation of the Standard C++ Library (in particular, string iterators) properly took this into account.

Stila answered 13/10, 2008 at 18:5 Comment(2)
std::reverse does NOT take this into account. It reverses value_type's. In the std::string case, it reverses char's. Not characters.Verdict
Say rather that we old school programmers know of DBCS but also know of UTF-8: for we all know that programmers are just like addicts when they say 'one more line and I'll quit!' I'm sure some programmers eventually quit but frankly programming really is like an addiction for me; I get withdrawals from not programming. It's a good point that you add here. I don't like C++ (I tried really really hard to like it even writing quite a bit of it but it still is for me aesthetically unappealing to say the least) so I can't comment there but you make a good point anyway so have a +1.Dilley
S
12

If you're looking for reversing NULL terminated buffers, most solutions posted here are OK. But, as Tim Farley already pointed out, these algorithms will work only if it's valid to assume that a string is semantically an array of bytes (i.e. single-byte strings), which is a wrong assumption, I think.

Take for example, the string "año" (year in Spanish).

The Unicode code points are 0x61, 0xf1, 0x6f.

Consider some of the most used encodings:

Latin1 / iso-8859-1 (single byte encoding, 1 character is 1 byte and vice versa):

Original:

0x61, 0xf1, 0x6f, 0x00

Reverse:

0x6f, 0xf1, 0x61, 0x00

The result is OK

UTF-8:

Original:

0x61, 0xc3, 0xb1, 0x6f, 0x00

Reverse:

0x6f, 0xb1, 0xc3, 0x61, 0x00

The result is gibberish and an illegal UTF-8 sequence

UTF-16 Big Endian:

Original:

0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00

The first byte will be treated as a NUL-terminator. No reversing will take place.

UTF-16 Little Endian:

Original:

0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00

The second byte will be treated as a NUL-terminator. The result will be 0x61, 0x00, a string containing the 'a' character.

Sorites answered 13/10, 2008 at 18:55 Comment(5)
std::reverse will work for two-byte unicode types, as long as you're using wstring.Migraine
I'm not very familiar with C++, but my guess is that any respectable standard library function dealing with strings will be able to handle different encodings, so I agree with you. By "these algorithms", I meant the ad-hoc reverse functions posted here.Sorites
Unfortunately, there's no such thing as "respectable function dealing with strings" in standard C++.Gelya
@Migraine If it reverses a surrogate pair, the result won't be correct anymore. Unicode is not actually a fixed-width charsetCorporator
What I like about this answer is that it shows the actual ordering of bytes (though endianness might be something - ah, I see you did indeed consider it) and how it is either correct or incorrect. But I think the answer would be improved if you incorporated some code demonstrating this.Dilley
I
8

Another C++ way (though I would probably use std::reverse() myself :) as being more expressive and faster)

str = std::string(str.rbegin(), str.rend());

The C way (more or less :) ) and please, be careful about XOR trick for swapping, compilers sometimes cannot optimize that.

In such case it is usually much slower.

char* reverse(char* s)
{
    char* beg = s, *end = s, tmp;
    while (*end) end++;
    while (end-- > beg)
    { 
        tmp  = *beg; 
        *beg++ = *end;  
        *end =  tmp;
    }
    return s;
} // fixed: check history for details, as those are interesting ones
Iconic answered 28/9, 2012 at 13:10 Comment(7)
I'd use strlen to find the end of the string, if it's potentially long. A good library implementation will use SIMD vectors to search faster than 1 byte per iteration. But for very short strings, while (*++end); will be done before a library function call gets started searching.Carine
@PeterCordes well, agreed, strlen should be used anyway for readability. For longer string you should always keep the length in a varialbe anyway. strlen on SIMD is usually given as an example with this disclaimer, that is not real life application, or at least it was 5 years ago, when the code was written. ;)Iconic
If you wanted this to run fast on real CPUs, you'd use SIMD shuffles to do the reverse in 16B chunks. :P e.g. on x86, _mm_shuffle_epi8 (PSHUFB) can reverse the order of a 16B vector, given the right shuffle control vector. It can probably run at nearly memcpy speed with some careful optimization, especially with AVX2.Carine
char* beg = s-1 has undefined behavior (at least if s points to the first element of an array, which is the most common case). while (*++end); has undefined behavior if s is an empty string.Rhigolene
@Iconic Well, you are making the claim that s-1 has defined behavior even if s points to the first element of an array, so it's you who should be able to cite the standard in support.Rhigolene
@Iconic Your code does not use the -- operator. I never said anything about types. Pointer arithmetic in general is only valid if it moves the pointer within the same array (or one element past the end). See also c-faq.com/aryptr/non0based.html, which explicitly says what you're doing is invalid.Rhigolene
@Rhigolene you are very wise indeed, you could have just fixed the answer as SO recommends. Nevertheless you are right I was wrong. I'll remove my comments in shame an humility, as they are no longer valid.Iconic
M
6
#include <cstdio>
#include <cstdlib>
#include <string>

void strrev(char *str)
{
        if( str == NULL )
                return;

        char *end_ptr = &str[strlen(str) - 1];
        char temp;
        while( end_ptr > str )
        {
                temp = *str;
                *str++ = *end_ptr;
                *end_ptr-- = temp;
        }
}

int main(int argc, char *argv[])
{
        char buffer[32];

        strcpy(buffer, "testing");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "a");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "abc");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "");
        strrev(buffer);
        printf("%s\n", buffer);

        strrev(NULL);

        return 0;
}

This code produces this output:

gnitset
a
cba
Microcopy answered 13/10, 2008 at 16:36 Comment(11)
@uvote, Don't use strcpy. Ever. If you have to use something like strcpy use strncpy. strcpy is dangerous. By the way C and C++ are two separate languages with separate facilities. I think you're using header files only available in C++ so do you really need an answer in C?Kaslik
If the string is empty, strlen will return 0 and you will index outside the array which is illegal in C (you can address one element past the end of an array but not one element before).Physiological
strcpy is perfectly safe if the programmer can keep track of the size of his arrays, many would argue that strncpy is less safe since it does not guarantee the resulting string is null terminated. In any case, there is nothing wrong with uvote's use of strcpy here.Physiological
@Onorio Catenacci, strcpy is not dangerous if you know that the source string will fit inside the destination buffer, as in the cases given in the above code. Also, strncpy zero-fills up to the number of chars specified in the size parameter if there is left-over room, which may not be desired.Lalapalooza
I would consider strcpy to be in the same class of potentially dangerous language features as operator overloading--fine and safe if someone knows how to use it properly but most developers don't know how to use it properly. Hence it should simply be avoided. I stand by my advice.Kaslik
Anyone that can't use strcpy properly shouldn't be programming in C.Physiological
@Robert Gamble, I agree. However, since I don't know of any way to keep people from programming in C no matter what their competence, I usually recommend against this.Kaslik
@Robert Gamble: Now there's an obscure C rule (indexing one before an array). Now that you mentioned it, I do remember it, and that's a good catch. But of course it works in gcc on x86, as my test shows. (and I'm OK with my use of strcpy)Microcopy
@OnorioCatenacci Rubbish. It's perfectly safe if you keep track of things. That black and white attitude you have comes from non-defensive programming and/or repeating what they've read/heard. It's simply not true. And just like Robert points out some - myself included - consider strncpy() less safe. You don't know a better way than to recommend against this? Why not recommend that they actually check for errors etc. instead? That's far more valuable than blacklisting something because some people use it incorrectly.Dilley
@Dilley I'd say that if the SEI thinks being careful about strcpy is a good thing then I agree: wiki.sei.cmu.edu/confluence/display/c/…. Strcpy relies on programmer discipline and potentially relies on knowledge that a programmer may simply not possess. There's a safer alternative--why not use it?Kaslik
@OnorioCatenacci Because it's not always safer maybe? Someone else already pointed this out. The point anyway is you can make anything unsafe if you want to. But if you know the correct size then guess what? You could also do testing right. This is common sense. Being careful with strcpy as you say isn't the same thing as your earlier Don't use strcpy. Ever.. Big difference. Huge difference. Your earlier comment is black and white. Being safe is not the same thing. Black and white is rarely a good idea. Life isn't that simple. strncpy can also create unterminated strings. Unsafe.Dilley
Z
4

In case you are using GLib, it has two functions for that, g_strreverse() and g_utf8_strreverse()

Zurek answered 14/10, 2008 at 5:24 Comment(0)
A
4

I like Evgeny's K&R answer. However, it is nice to see a version using pointers. Otherwise, it's essentially the same:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *reverse(char *str) {
    if( str == NULL || !(*str) ) return NULL;
    int i, j = strlen(str)-1;
    char *sallocd;
    sallocd = malloc(sizeof(char) * (j+1));
    for(i=0; j>=0; i++, j--) {
        *(sallocd+i) = *(str+j);
    }
    return sallocd;
}

int main(void) {
    char *s = "a man a plan a canal panama";
    char *sret = reverse(s);
    printf("%s\n", reverse(sret));
    free(sret);
    return 0;
}
Azole answered 15/3, 2011 at 17:33 Comment(0)
J
4

Recursive function to reverse a string in place (no extra buffer, malloc).

Short, sexy code. Bad, bad stack usage.

#include <stdio.h>

/* Store the each value and move to next char going down
 * the stack. Assign value to start ptr and increment 
 * when coming back up the stack (return).
 * Neat code, horrible stack usage.
 *
 * val - value of current pointer.
 * s - start pointer
 * n - next char pointer in string.
 */
char *reverse_r(char val, char *s, char *n)
{
    if (*n)
        s = reverse_r(*n, s, n+1);
   *s = val;
   return s+1;
}

/*
 * expect the string to be passed as argv[1]
 */
int main(int argc, char *argv[])
{
    char *aString;

    if (argc < 2)
    {
        printf("Usage: RSIP <string>\n");
        return 0;
    }

    aString = argv[1];
    printf("String to reverse: %s\n", aString );

    reverse_r(*aString, aString, aString+1); 
    printf("Reversed String:   %s\n", aString );

    return 0;
}
Jumbo answered 22/2, 2012 at 10:39 Comment(4)
That's a quite fun solution, you should add some tracing like printf("%*s > [%d] reverse_r('%c', %p=\"%s\", %p=\"%s\")\n", depth, " ", depth, val, s, (s ? s : "null"), n, (n ? n : "null")); at the start and < at the end.Anagnorisis
Pushing each char onto the stack does not count as "in place". Especially not when you're actually pushing 4 * 8B per character (on a 64-bit machine: 3 args + a return address).Carine
The original question is "How do you reverse a string in C or C++ without requiring a separate buffer to hold the reversed string?" - there was no requirement to swap 'in place'. Also,this solution mentions the bad stack usage from the start. Am I being down voted because of other people's poor reading comprehension?Jumbo
I wouldn't call it 'sexy' but I would say that it's demonstrative and instructional. If recursion is used properly it can be very valuable. However it should be pointed out that - last I knew - C does not even require a stack per se; however it does recursion. Either way it's an example of recursion which if used properly can be very useful and valuable. I don't think I've ever seen it used to reverse a string though.Dilley
B
2

If you are using ATL/MFC CString, simply call CString::MakeReverse().

Brezhnev answered 31/10, 2013 at 21:40 Comment(0)
C
0

Yet another:

#include <stdio.h>
#include <strings.h>

int main(int argc, char **argv) {

  char *reverse = argv[argc-1];
  char *left = reverse;
  int length = strlen(reverse);
  char *right = reverse+length-1;
  char temp;

  while(right-left>=1){

    temp=*left;
    *left=*right;
    *right=temp;
    ++left;
    --right;

  }

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

}
Conchita answered 8/6, 2012 at 19:40 Comment(1)
This demonstrates pointer arithmetic, much like my answer, but combines it with Swap. I believe this answer adds a lot, actually. You should be able to understand this type of code before adding a billion libraries as dependencies just to get some simple text box (something I see way too often in modern applications I get to work with)Grassi
M
0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

unsigned char * utf8_reverse(const unsigned char *, int);
void assert_true(bool);

int main(void)
{
    unsigned char str[] = "mañana mañana";
    unsigned char *ret = utf8_reverse(str,  strlen((const char *) str) + 1);

    printf("%s\n", ret);
    assert_true(0 == strncmp((const char *) ret, "anãnam anañam", strlen("anãnam anañam") + 1));

    free(ret);

    return EXIT_SUCCESS;
}

unsigned char * utf8_reverse(const unsigned char *str, int size)
{
    unsigned char *ret = calloc(size, sizeof(unsigned char*));
    int ret_size = 0;
    int pos = size - 2;
    int char_size = 0;

    if (str ==  NULL) {
        fprintf(stderr, "failed to allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    while (pos > -1) {

        if (str[pos] < 0x80) {
            char_size = 1;
        } else if (pos > 0 && str[pos - 1] > 0xC1 && str[pos - 1] < 0xE0) {
            char_size = 2;
        } else if (pos > 1 && str[pos - 2] > 0xDF && str[pos - 2] < 0xF0) {
            char_size = 3;
        } else if (pos > 2 && str[pos - 3] > 0xEF && str[pos - 3] < 0xF5) {
            char_size = 4;
        } else {
            char_size = 1;
        }

        pos -= char_size;
        memcpy(ret + ret_size, str + pos + 1, char_size);
        ret_size += char_size;
    }    

    ret[ret_size] = '\0';

    return ret;
}

void assert_true(bool boolean)
{
    puts(boolean == true ? "true" : "false");
}
Metamer answered 30/10, 2013 at 5:9 Comment(0)
C
0

C++ multi-byte UTF-8 reverser

My thought is that you can never just swap ends, you must always move from beginning-to-end, move through the string and look for "how many bytes will this character require?" I attach the character starting at the original end position, and remove the character from the front of the string.

void StringReverser(std::string *original)
{
  int eos = original->length() - 1;
  while (eos > 0) {
    char c = (*original)[0];
    int characterBytes;
    switch( (c & 0xF0) >> 4 ) {
    case 0xC:
    case 0xD: /* U+000080-U+0007FF: two bytes. */
      characterBytes = 2;
      break;
    case 0xE: /* U+000800-U+00FFFF: three bytes. */
      characterBytes = 3;
      break;
    case 0xF: /* U+010000-U+10FFFF: four bytes. */
      characterBytes = 4;
      break;
    default:
      characterBytes = 1;
      break;
    }

    for (int i = 0; i < characterBytes; i++) {
      original->insert(eos+i, 1, (*original)[i]);
    }
    original->erase(0, characterBytes);
    eos -= characterBytes;
  }
}
Chiarra answered 27/1, 2021 at 21:27 Comment(0)
D
0
void reverseString(vector<char>& s) {
        int l = s.size();
        char ch ;
        int i = 0 ;
        int j = l-1;
        while(i < j){
                s[i] = s[i]^s[j];
                s[j] = s[i]^s[j];
                s[i] = s[i]^s[j];
                i++;
                j--;
        }
        for(char c : s)
                cout <<c ;
        cout<< endl;
}
Dibbell answered 5/2, 2021 at 21:0 Comment(0)
N
0

input string, return string, No other library required

std::string reverse_string(std::string &str)
{   
  const char*buf = str.c_str();
  char *start = const_cast<char*>(buf);
  char *end = start + strlen(buf) - 1;
  char t;

  while(start < end)
  {
      t = *start;
      *start = *end;
      *end = t;
      start ++;
      end --;
  }
  str = buf;
  return str;
}
std::string md1 = "abcdefghijklmnopqrstuvwxyz0123456789";
std::cout << reverse_string(md1) << std::endl;

//9876543210zyxwvutsrqponmlkjihgfedcba
Nuisance answered 2/9, 2022 at 15:25 Comment(0)
G
-1

If you don't need to store it, you can reduce the time spent like this:

void showReverse(char s[], int length)
{
    printf("Reversed String without storing is ");
    //could use another variable to test for length, keeping length whole.
    //assumes contiguous memory
    for (; length > 0; length--)
    {
        printf("%c", *(s+ length-1) );
    }
    printf("\n");
}
Grassi answered 17/5, 2014 at 1:46 Comment(1)
I seem to be the only answer that doesn't have a buffer, or temp variable. I use length of string, but the others that do this add another one for the head (vs tail). I'm assuming the standard reverse func stores the variable, via a swap or something. Thus, I am under the impression, assuming pointer math and UTF type line up, that this is possibly the only answer that actually answers the question. The added printf()'s can be taken out, I just did it to look nicer for the result. I wrote this for speed. No extra allocations or vars. Probably the fastest algorithm for displaying reverse str()Grassi
F
-1

In C++ the reverse can be done in a function:

#include <algorithm>
#include <string>

void backwards(vector<string> &inputs_ref) {
    for (auto i = inputs_ref.begin(); i != inputs_ref.end(); ++i) {
        reverse(i->begin(), i->end());
    }
}
Felix answered 14/11, 2021 at 11:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.