0

In this sample program, why the value of the data pointed by the iterator is kept, even after the list is empty?
Is it something bound to happen due to the implementation of iterators in C++ (i.e. the value of the object is kept into the iterator) or is it because the segment of the memory was declared as free for used, but hasn't been changed yet?

#include <iostream>
#include <list>
using namespace std;
int main ()
{
    list<int> mylist;
    list<int>::iterator it = mylist.begin();
    cout << "printing: " << *it << endl;

    mylist.push_back(77);
    mylist.push_back(22);

    // now front equals 77, and back 22

    mylist.front() -= mylist.back();
    it = mylist.begin();
    cout << "printing: " << *it << endl;
    cout << "mylist.front() is now " << mylist.front() << '\n';
    // trying to delete all elements and then see how the iterators are handling it
    it = mylist.begin();
    cout << "printing: " << *it << endl;
    mylist.remove(55);
    cout << "printing: " << *it << endl;
    mylist.remove(22);
    cout << "printing: " << *it << endl;
    cout << "mylist size: " << mylist.size() << endl;
    cout << "mylist.front() is now " << mylist.front() << '\n';
    cout << "printing: " << *it << endl;

    return 0;
}

And this is the output:

printing: 495034304
printing: 55
mylist.front() is now 55
printing: 55
printing: 55
printing: 55
mylist size: 0
mylist.front() is now 38375440
printing: 55
Chris
  • 3,619
  • 8
  • 44
  • 64
  • 6
    Accessing `front` when your list is empty is UB - it might happen the old data is still there. Or demons may fly out of your nose. – doctorlove Jan 14 '14 at 16:18
  • It is just undefined behaviour. The iterator has been invalidated. – juanchopanza Jan 14 '14 at 16:19
  • @doctorlove: Hahah great answer, but I am no wondering about the front of the list by the `*it` which prints 55 even after its removal from the list. – Chris Jan 14 '14 at 16:20
  • 3
    "It is because the segment of the memory was declared as free for used, but hasn't been changed yet" (and because you've been lucky not to crash your program or format your hard drive by invoking UB). – DevSolar Jan 14 '14 at 16:20
  • @abiessu: This is how it is used here, http://www.cplusplus.com/reference/list/list/front/, it's merely the example, but a bit modified. – Chris Jan 14 '14 at 16:21
  • @abiessu That won't cause UB. Why would it? – juanchopanza Jan 14 '14 at 16:22
  • @DevSolar: "format your hard drive by invoking UB", such thing can happen? :P – Chris Jan 14 '14 at 16:22
  • @juanchopanza: Which one? The `*it`? – Chris Jan 14 '14 at 16:23
  • @Chris both the access to `front()` when the thing is empty, and the `*it`. – juanchopanza Jan 14 '14 at 16:24
  • possible duplicate of [Can a local variable's memory be accessed outside its scope?](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) -- This is the best description that I have ever read – David Rodríguez - dribeas Jan 14 '14 at 16:40
  • @Chris: It's an exaggeration. But too many careless programmers think that the worst thing that could happen when you invoke UB is a crash... The chance of the equivalent of `system( "sudo sh -c \"cat /dev/random /dev/sda\"" )` existing somewhere in your process memory by accident and a random stack trashing jumping to just that address is remote, but it's a possibility. But when you're taking malicious intent into account, undefined behaviour in a program can be exploited. My comment aimed at ramping up the paranoia a bit so that people learn not to take UB lightly. – DevSolar Jan 14 '14 at 19:42
  • @dribeas: not a duplicate, while the answer may be same, the question is different. – DarkWanderer Jan 14 '14 at 20:05
  • @DarkWanderer: I totally agree. – Chris Jan 14 '14 at 20:07
  • @DevSolar: Yes, I know that when UB hasn't been taken care of, it can lead to exploits. Indeed, I think that's a really small possibility. Thank you for the example with stack trashing! – Chris Jan 14 '14 at 20:09
  • http://stackoverflow.com/q/6441218/560648 – Lightness Races in Orbit Jan 15 '14 at 02:33

3 Answers3

6

Is it something bound to happen due to the implementation of iterators in C++

No, it's undefined behaviour. The iterator has become invalid, and can't be used.

is it because the segment of the memory was declared as free for used, but hasn't been changed yet?

Yes, that's why you observed what you observed. But the memory could be reused for something else, or made inaccessible - you can't rely on any observations of undefined behaviour.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
3

Iterator is invalidated by you operations, but it may still point to memory with the previous value. Anyway, accessing it after the value is removed from the list is undefined behaviour.

Wojtek Surowka
  • 20,535
  • 4
  • 44
  • 51
  • No memory leaks, just accessing something which is not valid anymore. – Wojtek Surowka Jan 14 '14 at 16:24
  • @Chris that is irrelevant. You have UB. There *could* be memory leaks, as well as other nasty things. – juanchopanza Jan 14 '14 at 16:25
  • @Chris - if you mean "I can still see the data, is that a leak?" then no, it's not a leak, the memory just hasn't been re-used yet. If you mean "Can I still use this data, after I deleted it, and not worry about memory leaks?" then no, as there's no guarantee how long that data will stick around after it's been deleted. – benjymous Jan 14 '14 at 16:30
  • @benjymous: (and Wojtek) I don't mean seeing the data to be a leak. I've checked with valgrind and no leaks were detected. Shouldn't this lead to a segmentation fault, since after the removal (and possible free) the memory isn't owned by my program? – Chris Jan 14 '14 at 16:31
  • It _MAY_ lead to a segfault. Or you might end up with the memory region still holding old but still valid data. This is why you avoid UB. It will work on your machine but not work on someone else's. See your comment about my code below. – Sean Perry Jan 14 '14 at 16:39
  • @SeanPerry: Surely, I know we should avoid UB. But why it *may* lead to a segfault? Could it be because of the MemoryManager scheduling policy on when to remove a *marked-free* memory segment from a process? – Chris Jan 14 '14 at 16:42
  • Because you have no idea what happened. That is why it is UB. The system _MIGHT_ come behind you and set every old pointer to zero. Or it might just put a random number there. Or it might do nothing. The first guy to teach me coding taught me to allocate a large array once and never again invoke malloc. This turned out to be helpful because I had to learn how to manage a bounded amount of memory. The memory subsystem might immediately reuse the region for another request. Or it might put it to the end of a queue. You just never know so you do not trust it. – Sean Perry Jan 14 '14 at 16:52
1
#include <stdio.h>

int main(int argc, char **argv)
{
    int *p = NULL;

    p = (int*)malloc(sizeof(int));

    *p = 5;

    printf("P: %d\n", *p);

    free(p);

    printf("P: %d\n", *p);
}

Why is this still a surprise? Marking a pointer as invalid has nothing to do with what is stored where it used to point.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
Sean Perry
  • 3,776
  • 1
  • 19
  • 31
  • I've tried adding your include to the segment code, but couldn't submit the edit cause it needed it to be more than 6 chars. Also, your code prints this `P: 5 P: 0` – Chris Jan 14 '14 at 16:29
  • That is EXACTLY the point. On my system I get 5 and 5. UB is UB. – Sean Perry Jan 14 '14 at 16:34
  • 1
    I made mine in C to make a point about pointers and how this is baked into the thinking of the language. You could just as easily do with with new/delete or with iterators as the original code does. – Sean Perry Jan 14 '14 at 16:36
  • I also experienced `P: 5 P: 0`, so it was most likely overwritten. If you allocate 1000 elements this becomes less likely so you probably get `P: 5 P: 5`: http://coliru.stacked-crooked.com/a/511758849e268b43 – leemes Jan 14 '14 at 16:36