5

I've been pretty confused while programming before, but this one takes the cake. Basically I set the value in one for loop, and in the following iteration it changes to the value of the next one.

for (int i = 0; i < 2; ++i)
{
    for (int j = 0; j < numWords[i]; ++j) //numWords [0] = 9, numWords [1] = 7
    {
        stb[i][j].word = const_cast<char*>(is (j + 1,1).c_str()); //is(int,length[opt]) converts int to string, c_str() returns const char *, but I need char *
        cout << is(j+1,1) << ' ' << stb[i][j].word << '\n';
    }
}

for (int i = 0; i < 2; ++i)
{
    for (int j = 0; j < numWords [i]; ++j)
    {
        cout << stb[i][j].word << ' ';
    }
    cout << '\n';
}

Output:

1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
1 1
2 2 
3 3
4 4
5 5 
6 6
7 7 
7 7 7 7 7 7 7 7 7
7 7 7 7 7 7 7

My only guess now is something with the const, but it doesn't make sense why it would keep changing all previous array elements...

chris
  • 60,560
  • 13
  • 143
  • 205
  • 3
    You have yet to show us `is()`. – Ignacio Vazquez-Abrams Sep 26 '11 at 05:17
  • Well, I actually just found the reason. First of all, is() works fine, but I just learned that since strings don't have to store each character in successive memory locations, as soon as the string you pass to cstr() changes, so does the char * you get from it. Go figure I spend how much time on this :/ – chris Sep 26 '11 at 05:25
  • 1
    Do NOT use `const_cast`. Ever. (By the time you have enough experience in C++ to know when it would be safe, you'll have learned better techniques.) – Ben Voigt Sep 26 '11 at 05:27
  • @chris - (in C++11) `std::string`s **are** required to store each character in contiguous memory. The `char*` that you get from `c_str()` cannot change, but the memory that it references can. The problem in this case, however, is not that the referenced memory is changing, but rather that you are reading from memory which has been invalidated by an operation on the `string` (the destruction of the `string`). This results in undefined behaviour. – Mankarse Sep 26 '11 at 05:47
  • If you are facing problems like this one, remember you can set [data breakpoints](http://stackoverflow.com/questions/621535/what-are-data-breakpoints) and the debugger will break where the data is being changed. This is not supported on all architectures. –  Sep 26 '11 at 06:05

1 Answers1

3

This is pretty simple. Your program has undefined behaviour (if my assumptions about is() are correct).

is(int, length) returns a std::string by value. You get a pointer to some internal structure in that string by using c_str(). This string is then destructed at the end of the full-expression. This destruction invalidates the pointers that you obtained from c_str().

This means that you fill up the array with pointers to invalid memory. You then read from these pointers to print out the contents of the array. Reading from invalid memory results in undefined behaviour.

A possible explanation for the observed behaviour is this:

Each string that is returns reuses the same memory. In the first loop you read from the memory before it has been overwritten by another call to is, and so you get the correct value. In the second loop you read from the memory after it has been overritten, and so you get the final value in the array.

Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • That makes sense. Now my problem is that nothing I try will work in the way it's intended. I need a char * as the word. – chris Sep 26 '11 at 05:38
  • 1
    @chris - make a separate `std::vector` or array that owns the strings, and then get the `char*`s from the strings that are in that array. – Mankarse Sep 26 '11 at 05:49