Reputation: 19258
The output of the program below is
1: foo strlen: 3
2: strlen: 0
3: foo strlen: 3
4: foo strlen: 3
5: strlen: 0
6: strlen: 0
I don't understand
1
prints the string, but 2
does not andThe source:
#include "stdafx.h"
#include <map>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
map<string, string> m;
m["foo"] = "bar";
const char * s;
for(map<string, string>::iterator it = m.begin(); it != m.end(); it++)
{
pair<string, string> kvPair = *it;
s = kvPair.first.c_str();
printf("1: %s strlen: %d\n", s, strlen(s));
break;
}
printf("2: %s strlen: %d\n", s, strlen(s));
for(map<string, string>::iterator it = m.begin(); it != m.end(); it++)
{
s = (*it).first.c_str();
printf("3: %s strlen: %d\n", s, strlen(s));
break;
}
printf("4: %s strlen: %d\n", s, strlen(s));
for(map<string, string>::iterator it = m.begin(); it != m.end(); it++)
{
s = ((pair<string, string>) (*it)).first.c_str();
printf("5: %s strlen: %d\n", s, strlen(s));
break;
}
printf("6: %s strlen: %d\n", s, strlen(s));
return 0;
}
Update An explanation for programmers with little C++ background would be appreciated.
Upvotes: 1
Views: 299
Reputation: 7995
In your first example, you call c_str()
on kvPair
which is declared in the scope of the for
-loop. The result becomes invalid when the for
loop is exited, because kvPair
is destroyed.
In the second example, you call c_str()
on the value in the map. The result only becomes invalid when the map is destroyed, which happens when _tmain(...)
returns.
In you third example, you call c_str()
on a temporary (created by the cast to pair), and that temporary is destroyed before printf("5...
is called.
Explanation
The pointer returned by c_str()
points to some memory owned by the string
it is called on, so when that string
is destroyed, accessing the pointer is undefined behaviour.
Upvotes: 5
Reputation: 154027
Partially by chance.
In 1/2 you create a local variable within the loop, copying the value
out of the map into kvPair
. You set s
to point to data in this
copy. The copy is destroyed (destructor called) when you exit the
block. By any means: break
, goto
, an exception, or simply finishing
the loop body and going through it again—each time through the
loop, you get a new kvPair
, which is destructed at the end of the loop
body. s
points to data inside kvPair.first
, and any use of s
(even simply copying it) after kvPair
has been destructed is undefined
behavior. Anything can happen, and what happens is likely to be
different depending on the level of debug checking and optimization, or
even depending on totally unrelated aspects of the program. (If you
consistently get an empty string, there is probably some poorly designed
debug checking going on. Well designed debug checking would cause an
immediate crash, so you would see the error.)
In 2/3, you initialize s
with the actual contents of the map, so it is
valid until the map is destructed, or the element is removed from the
map.
In 4/5, you create a temporary: T( initialization )
constructs a
temporary of type T
, using the initialization given, for any type T
,
including type std::pair<std::string, std::string>
. (This isn't quite
true; if T
is a reference, for example, the behavior is different.)
And you initialize s
to point to data within this temporary. The
lifetime of a temporary is only to the end of the full expression which
contains it, so the contents of s
become invalid at the semicolon
ending the statement (which in this case is the end of the full
expression). As in 1/2, undefined behavior ensues when you use s
after this.
Upvotes: 3
Reputation: 29266
The first loop s
is pointing to data in the pair - the pair goes out of scope after the loop, so your data is bogus
The second and third loops have s
is pointing to data in the actual collection - so it stays in scope
Upvotes: 0