0

The code below compiles on Linux (g++ 4.8.4) and Windows (VS Express 2013) but produces different results when executed.

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char* argv[])
{
    string str = str;
    cout << "'str.length()': "
         << str.length() << endl;
    return 0;
}

On Linux I get:

'str.length()': 140187593065792

On Windows I get:

'str.length()': 0

In addition, if I change the initialization statement as shown below then it complies but it crashes at runtime (On Linux I get: terminate called after throwing an instance of 'std::bad_alloc'):

string str = str + str;

I understand that, in both cases, this is not the usual way one would initialize a string variable. However, could someone please explain what is really wrong with this code? Also, why the output is different in the first case?

3 Answers3

6

When the initialiser is evaluated, str has been declared but it has not yet been constructed in any fashion. Its "value" is completely indeterminate. Your program has undefined behaviour.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I am not sure that compilers would warn about this, it is perfectly fine to pass a pointer or reference to an object that has not been initialized around. Compilers can inline the function and track the lifetime in this case (where the constructor is `inline`) but in the general case I would not expect it. – David Rodríguez - dribeas Mar 13 '16 at 18:48
  • @DavidRodríguez-dribeas: `str` is neither a pointer nor a reference, and this is not the general case. That being said, I gave it a shot with GCC and it did not warn. – Lightness Races in Orbit Mar 13 '16 at 18:53
  • The selected constructor would be the one taking a `std::string const&`, which *is* a reference. – David Rodríguez - dribeas Mar 13 '16 at 18:54
  • @David: Oh, right. Yeah, okay. I'd expect to [get a warning for a built-in](http://coliru.stacked-crooked.com/a/774afd85559f0712), though. – Lightness Races in Orbit Mar 13 '16 at 18:55
4

This code is deep in the land of undefined behavior. Formally, once the name of the object has been seen, it's in scope and can be used as an initializer. But since its constructor hasn't run, the object itself hasn't been initialized sensibly, and copy constructing it on top of itself won't necessarily turn it into a valid object. Whatever happens happens; don't do that.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
1

From formal point of view, it's undefined behavior and you may end on that.

From practical point of view, when you declare local instance of string, its memory has random values. So, the pointer to array of characters is invalid, length is invalid, etc.

Why results are different in VS and g++? You probably run your VS program in Debug mode and VS may initialize some things What are some reasons a Release build would run differently than a Debug build

Community
  • 1
  • 1
  • 1
    The reason we stick with the formal observations is that the practical evidence is potentially far more complex and unpredictable than simply observing bytes in memory. The compiler could have taken advantage of optimisation opportunities in this code that are broken by the self-initialisation, effectively corrupting the code itself! Read https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=633 – Lightness Races in Orbit Mar 13 '16 at 18:54