Evgeniy Berezovsky
Evgeniy Berezovsky

Reputation: 19258

C++ iterator conundrum

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

The 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

Answers (3)

Kleist
Kleist

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

James Kanze
James Kanze

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

John3136
John3136

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

Related Questions