Why it works: returning a string literal C from the std :: string function and calling c_str ()

Recently, we had a lecture in college, where our professor told us about different things, to be careful when programming in different languages. The following is an example in C ++:

std::string myFunction() { return "it me!!"; } int main(int argc, const char * argv[]) { const char* tempString = myFunction().c_str(); char myNewString[100] = "Who is it?? - "; strcat(myNewString, tempString); printf("The string: %s", myNewString); return 0; } 

The idea of ​​why this will lead to an error is that return "it me!!" implicitly calls the constructor std :: string with char []. This string is returned from the function, and the c_str() function returns a pointer to the data from std::string .

Since the string returned from the function is not mentioned anywhere, it should be freed immediately. It was a theory.

However, running this code is no problem. It would be interesting to know what you think. Thank!

+42
c ++
Jan 02 '14 at 12:38 on
source share
9 answers

Your analysis is correct. You have undefined behavior. This means that everything can happen. It seems that in your case, the memory used for the string, although allocated, still retains the original content when accessing it. This often happens because the OS does not clear the allocated memory. It just means that it is available for future use. This is not what the C ++ language should deal with: it is really a detail of the OS implementation. As for C ++, the behavior is "undefined for all" catch-all ".

+44
Jan 02 '14 at 12:40
source share

I assume that freeing up does not mean clearing or nulling the memory. And obviously, this can lead to segfault in other circumstances.

+5
Jan 02 '14 at
source share

As mentioned by the C ++ standard, this behavior is undefined.

The reason this β€œworks” is because the memory was returned to the heap manager, who is holding onto it for later reuse. The memory was not returned to the OS and thus still belongs to the process. Therefore, access to free memory does not cause segmentation errors. However, the problem remains that now two parts of your program (your code and the heap manager or the new owner) gain access to the memory, which, in their opinion, clearly belongs to them. It fucks things sooner or later.

+3
Jan 07 '14 at 19:48
source share

I think the reason is that the stack memory is not overwritten, so it can get the original data. I created a test function and called it before strcat.

 std::string myFunction() { return "it me!!"; } void test() { std::string str = "this is my class"; std::string hi = "hahahahahaha"; return; } int main(int argc, const char * argv[]) { const char* tempString = myFunction().c_str(); test(); char myNewString[100] = "Who is it?? - "; strcat(myNewString, tempString); printf("The string: %s\n", myNewString); return 0; } 

And get the result:

 The string: Who is it?? - hahahahahaha 

It proved my idea.

+2
Jan 13 '14 at 3:34
source share

The fact that the line is freed does not necessarily mean that the memory is no longer available. While you are not doing anything to overwrite it, memory is still available.

+1
Jan 02
source share

As stated above, this is unpredictable behavior. This does not work for me (in Debug configuration). std :: string The destructor is called immediately after assignment to tempString - when the expression using the temporary line object ends. Leaving tempString to point to the released memory (in your case there are still literals "it me !!").

+1
Jan 08 '14 at 8:29
source share

You cannot conclude that there is no problem getting your result by coincidence.

There are other ways to detect "problems":

  • Static Analysis
  • Valgrind will catch an error, showing you both the offending action (trying to copy from the freed zone - by strcat) and the release that caused the release.

Invalid read of size 1

  at 0x40265BD: strcat (mc_replace_strmem.c:262) by 0x80A5BDB: main() (valgrind_sample_for_so.cpp:20) [...] Address 0x5be236d is 13 bytes inside a block of size 55 free'd at 0x4024B46: operator delete(void*) (vg_replace_malloc.c:480) by 0x563E6BC: std::string::_Rep::_M_destroy(std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.13) by 0x80A5C18: main() (basic_string.h:236) [...] 

code>

  • The only correct way would be to prove the correctness of the program. But it is very difficult for a procedural language, and C ++ makes it harder.
+1
Jan 08 '14 at
source share

If I am missing something, I think this is a problem of scale. myFunction () returns std :: string. A string object is not directly bound to a variable. But it remains in scope until the end of main (). Thus, tempString will indicate completely valid and available memory space until the end of the main () code of the block, at which time tempString will also fall out of scope.

0
Jan 08 '14 at 19:19
source share

Actually, string literals have a static storage duration. They are packaged inside the executable itself. They are not on the stack and are not allocated dynamically. In the usual case, it’s correct that this indicates invalid memory and undefined behavior, however for strings the memory is in static storage, so it will always be valid.

-one
Jan 02 '14 at 13:02
source share



All Articles