views:

12029

answers:

10

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

+2  A: 
#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
@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?
Onorio Catenacci
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).
Robert Gamble
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.
Robert Gamble
@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.
Chris Young
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.
Onorio Catenacci
Anyone that can't use strcpy properly shouldn't be programming in C.
Robert Gamble
@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.
Onorio Catenacci
@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)
+52  A: 
std::reverse(str.begin(), str.end());

This is the simplest way in C++.

Greg Rogers
This has way too many upvotes. It's only a sentence answer.
orlandu63
simple question, simple answer...
Greg Rogers
Why would the answer need to be any longer?
korona
+14  A: 

You use std::reverse algorithm from the C++ Standard Library.

Nemanja Trifunovic
Standard Template Library
Federico Ramponi
Standard Template Library is a pre-standard term. The C++ standard does not mention it, and former STL components are in the C++ Standard Library.
Nemanja Trifunovic
right. i always wonder why so many people still call it "STL" even though it just serves to confuse the matter. would be nicer if more people are like you and call it just "C++ Standard Library" or STL and say "STandard Library" :)
Johannes Schaub - litb
+18  A: 

Evil C:

#include <stdio.h>

void strrev(char *p)
{
  char *q = p;
  while(q && *q) ++q;
  for(--q; p < q; ++p, --q)
    *p = *p ^ *q,
    *q = *p ^ *q,
    *p = *p ^ *q;
}

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;
}

(This is XOR-swap thing. Take care to note that you must avoid swapping with self, because a^a==0.)


Ok, fine, let's fix the UTF-8 chars...

#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)

    $ ./strrev Räksmörgås ░▒▓○◔◑◕●

    ░▒▓○◔◑◕● ●◕◑◔○▓▒░

    Räksmörgås sågrömskäR

    ./strrev verrts/.

Anders Eurenius
There's no good reason to use XOR swap outside of an obfuscated code competition.
Chris Conway
1. XOR-swap can swap arbitrary size elements without allocating any intermediate storage. (Admittedly not relevant.)2. I *like* it.
Anders Eurenius
Also:3. Can be done in a macro without kinky block-macro or typeof stuff.
Anders Eurenius
Why not "*p ^= *q, *q ^= *p, *p ^= *q"?
Chris Conway
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.
Bill K
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?
Chris Conway
@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.
Konrad Rudolph
@All: Baah.. (No, really, you're being too serious!)@Chris Conway: Yes, I should've used ^=, but I've been hanging out in some dictatorships that frown on that sort of thing lately... :-P
Anders Eurenius
Anders Eurenius
Oh no, Räksmörgås! Now I have to go to the fridge and make me one. :P
Jonas Gulle
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). http://stackoverflow.com/questions/199260/reversing-a-utf-8-string-in-place
Juan Pablo Califano
Not upvoting this until the xor swap goes away.
Adam Rosenfield
+14  A: 

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--;
  }
}
Chris Conway
You don't need the stddef.h and string.h headers.
Robert Gamble
I need stddef, but not stdio.
Chris Conway
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.
QuantumPete
Fair enough. I was trying (and failing) to avoid the off-by-one error in @uvote's answer.
Chris Conway
+6  A: 

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.

Tim Farley
std::reverse does NOT take this into account. It reverses value_type's. In the std::string case, it reverses char's. Not characters.
MSalters
+7  A: 

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));
}
Eclipse
+4  A: 

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.

Juan Pablo Califano
std::reverse will work for two-byte unicode types, as long as you're using wstring.
Eclipse
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.
Juan Pablo Califano
+13  A: 

Read Kernigham and Richie

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

      for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
         c = s[i];
         s[i] = s[j];
         s[j] = c;
      }
}
+1  A: 

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

dmityugov