0

I have the following code in c# which reverses a string:

char[] charArray = s.ToCharArray();
int len = s.Length - 1;

for (int i = 0; i < len; i++, len--)
{
   charArray[i] ^= charArray[len];
   charArray[len] ^= charArray[i];
   charArray[i] ^= charArray[len];
}

return new string(charArray);

I am trying to convert it to C++ as an intellectual exercise more than anything. Here is what I have so far:

void main(void)
{
    char* str = "testing";
    char* result;
    int len;

    len = strlen(str);

    if (len <= 12)
    {
        result = new char[strlen(str)];
        for (int i = 0; i < len; i++, len--)
        {
             result[i] ^= str[len];
             result[len] ^= str[i];
             result[i] ^= str[len];
        }
    }
    else{
        std::reverse(str, &str[strlen(str)]);
    }

    cout << endl << result << endl;

    // cleanup 
    str = NULL;
    result = NULL;
} 

In .Net if the string is <= 12 (I think it's twelve) xor is faster than array reverse. Source - Sam Saffron I am basically trying to see if it still holds up in C++.

The string comes out in a weird format (════╣¥¿ë²²² to be precise).

Any ideas?

Note: I know the else statement doesn't work, I'll figure that bit out after ;)

Note 2: I am probably doing this completely wrong, so feel free to point absolutely anything out

Update

Thanks to everyone that has participated. I haven't played with c++ in a fair few years (and it shows) and thought it would be quiet easy to convert but obviously not. Think it's best I abandon this idea. Thanks again

Community
  • 1
  • 1
Stuart Blackler
  • 3,732
  • 5
  • 35
  • 60
  • 5
    This is an attempt at performance and yet you're using C-strings, xor swap, heap allocation, indexs instead of iterators, and calling strlen twice? – Pubby Feb 07 '12 at 00:11
  • 2
    Hint: What are the initial contents of the *result* buffer guaranteed to be in C++? – Eric Lippert Feb 07 '12 at 00:15
  • Just added source of reference. I'll fully admit I don't know what I'm doing. Purely intellectual exercise :) I am caching it, but not using it for some reason :/ – Stuart Blackler Feb 07 '12 at 00:15
  • 2
    Also, keep in mind that `result = NULL;` does not deallocate memory, and `char* str = "testing";` is not allocating any. – Pubby Feb 07 '12 at 00:15
  • 4
    @Pubby is entirely correct -- your "cleanup" code does no such thing. You're not in C# anymore; actually *cleaning up after yourself* is now your business, as is *ensuring that memory is initialized to sensible values.* – Eric Lippert Feb 07 '12 at 00:17
  • @EricLippert effectively a whole load of rubbish (iirc, what ever was in that location beforehand?)? So when I xor it I will get a whole load of rubbish back out? – Stuart Blackler Feb 07 '12 at 00:21
  • 1
    @StuartBlackler: Yep. You're xoring data with garbage; the result is garbage. – Eric Lippert Feb 07 '12 at 00:23

3 Answers3

2

A few things:

result = new char[strlen(str)];

Should be

result = new char[len + 1];

len because you've already calculated the length of str, and + 1 to make room for the NUL terminator.

Secondly, you need to copy the string into result before operating on it, because otherwise your array is full of garbage otherwise:

strcpy(result, str);

Thirdly,

std::reverse(str, &str[strlen(str)]);

Is wrong for two reasons: one, because you can't modify string literals, and two, because you should be using result:

std::reverse(result, result + len);

But if you do that, you also need to copy str into result first.

And lastly, setting a pointer to NULL does not deallocate the memory it points to. You have to

delete[] result; // delete[] because new[]

Note that for this to work even when the else is taken (and therefore result is not made to point to allocated memory), you need to do

char* result = NULL; // delete[] is defined as a nop on NULL pointers

All the above applies if you're sure you want to use C-strings. Once you get the hang of pointers, you can graduate to std::string:

std::string str("testing");

std::reverse(std::begin(str), std::end(str)); // or if you don't want to do it in-place,
                                              // std::string result(str.rbegin(), str.rend());
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Thanks for the info. Would I need to `delete[] str` as well? I tried it but it throw's an error? – Stuart Blackler Feb 07 '12 at 00:35
  • 1
    @StuartBlackler no, because it was not allocated with `new[]`. The rule is: **Exactly one `delete` for every `new`, and exactly one `delete[]` for every `new[]`**. It turns out that string literals (usually) reside in the memory of the executable and last the entire program (usually in read-only memory, but you're not allowed to modify them even if the permissions of the memory are not explicitly read-only), which is why you can't modify it. – Seth Carnegie Feb 07 '12 at 00:37
2

xor swap is for swapping. If you're copying into a result array then that's assigning, not swapping. Also, you must only iterate half-way through the array, otherwise you swap it twice.

Here is a translation of the C# code:

#include <iostream>
#include <algorithm>

int main(void)
{
    char str[] = "testing"; // arrays have automatic storage - no need to new/delete
    const size_t str_len = sizeof(str)-1; // sizeof(str) returns size of the array

    if (str_len <= 12) // because str_len is a constant expression, the other branch will be compiled-out
    {
        // this should probably use iterators (pointers) but oh well
        for (size_t i = 0, len = str_len-1; i < str_len/2; i++, len--)
        {
             str[i]   ^= str[len];
             str[len] ^= str[i];
             str[i]   ^= str[len];
        }
    }
    else{
        std::reverse(str, str + str_len); // str decays to a pointer
    }

   std::cout << str << '\n'; // don't use endl if you don't need to flush
}

This is pretty bad code. Just use std::string and std::reverse. It is faster than xor and only 2 lines long.

std::string str = "testing"
std::reverse(str.begin(), str.end());
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • I think I am going to give up on this intellectual exercise given the wealth of information here (thought it would be easier to convert to c++ but obviously not). May I ask why is the XOR swap check required? – Stuart Blackler Feb 07 '12 at 00:51
  • 2
    @Stuart : A number XOR itself is always 0. – ildjarn Feb 07 '12 at 00:52
  • @StuartBlackler it's not required but saves you from unnecessary xors (no point in swapping a letter with the same letter). – Seth Carnegie Feb 07 '12 at 00:57
  • @SethCarnegie It's entirely necessary as ildjarn has said. – Pubby Feb 07 '12 at 01:01
  • @StuartBlacker If you're wondering, xor has been shown to be less efficient than the typical swap-into-3rd-variable (`std::swap`). I'm sure you can a SO answer on this. – Pubby Feb 07 '12 at 01:02
  • @Pubby as long as the values are not at the same address, [the check is not required](http://ideone.com/pjXe0) – Seth Carnegie Feb 07 '12 at 01:02
  • @SethCarnegie Ah, so it's only for same address. Thanks for the info. – Pubby Feb 07 '12 at 01:05
1

A better way of doing it, more C++, less C

std::string mystring = "testing";

std::string reversed;

for(std::string::iterator str_it = mystring.rbegin(); str_it != mystring.rend(); ++str_it)
{
  reversed += *str_it;
}

std::cout << reversed << std::endl;
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415