10

I'm facing a very very strange behavior my application.

I'm going to describe my situation and then explain what is going wrong.

Situation

I have a method with a signature like this:

const StructureDef *getStructure(const std::string &theme, int index);

And I call it in this piece of code:

const StructureDef *sDef = 0;
do 
{
    sDef = ss->getStructure(theme, rand() % ss->availableStructureCount());
} while (!sDef);

I'm using this do-while structure, because the return value of the getStructure method might be NULL, depending on the combination of theme and index. So basically, what it does is asking random structures until we get a valid one. (If you want to know the detail, take a look at the screenshots.)

The method iterates over a std::vector<StructureDef> using it's ::iterator. And for each StructureDef, it checks if the structure belongs to that theme. If so, post-increase the counter and check if it is equal to the requested index. Like this:

// inside the loop
if (i++ == index)

When this if succeeds, the current StructureDef * is returned:

return sDef;

What is going wrong

I'm using XCode 4.4 it's debugger to see step by step what is going on, which is basically gdb.

The method I explained first, find a StructureDef * that matches my needs. So it returns that pointer. Here is a screenshot of the moment just before it is going to return in the debugger:

Return point in the debugger

(The line after the for-loop is simply return 0;)

Here the pointer sDef * points to 0x1d563270, which is where the correct instance of the StructureDef is located.

Next screenshot is what we get in the piece of code where I called that method:

Caller gets wrong address

As you can see, the pointer sDef *, which got the return value of the method, now points to 0x2fe03804. This is not what the method returned at all! I'm thinking that this is pointer points to somewhere on the stack, instead of the heap. (It should be the heap since the std::vector class stores its objects on the heap, right?).

I can't use Valgrind yet, since I'm on Mac OS X 10.8, which isn't supported by Valgrind.

I'm totally surprised by this behavior. I can't see why this is happening... Can it be that my compiler is broken, or is it doing some strange "optimization"?

Thanks in advance!
Martijn


To clarify DeadMG's comment:

I'm using different themes:

iron
wood
ice

etc...

My identifiers look like this:

iron__downside_touch_and_go
iron__platform__700_65
iron__wall_bang
wood__platform__600_40

etc... What I want to do is pick the structure with a specific index within one theme. So not the index the set of structures of all themes together, but the index of the subset of one theme. Take a look at the piece of code again :)


Update!!!

I gave wrong info. The vector is of the type std::vector<StructureDef>!! It stores objects. Not pointers!

So what (I think) I'm doing with the .operator->() call is the same as: &(*it). And it looks like it is working. To me it looked a bit stupid to write & and * after each other.


@Ben Voigt:

Architecture: enter image description here

Optimisation: enter image description here

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • You know, your whole function appears to be `structureDefs[i].identifier == (theme + "__")`, right? – Puppy Jul 10 '12 at 13:56
  • 2
    A first guess would be, that you are missing a `return` statement. This usually leads to strange return values. – pmr Jul 10 '12 at 13:57
  • 2
    @pmr, he has the return statement, take a look at the pictures. My guess is that in the line `const StructureDef *sDef= it.operator()` you are not getting the real address of the structure, but some iterator-related address. Can you work with the pointer after the function exits, or is it broken? – SingerOfTheFall Jul 10 '12 at 14:01
  • 6
    Probably off-topic, but using reserved names like `theme__` is a bad idea. – Mike Seymour Jul 10 '12 at 14:02
  • @SingerOfTheFall: I don't think that that is the problem, because the sDef pointer within the method got assigned correctly. What the `iterator.operator->()` does is simply returning the value behind the iterator. – Martijn Courteaux Jul 10 '12 at 14:03
  • @MartijnCourteaux, it can't be returning the _value_ because you assign a _pointer_ to it. – SingerOfTheFall Jul 10 '12 at 14:04
  • 3
    @SingerOfTheFall From what I can see, he uses weird syntax to dereference the iterator (why he doesn't use (*iterator) like regular people is beyond me :L ) but it is legal syntax and shouldn't cause the pointer to spontaneously change. If it is, wow, spooky action at a distance... FROM TEH FUTURE. Try a make clean. – Wug Jul 10 '12 at 14:04
  • @Wug, the syntax `iterator.operator->()` is kinda weird though. That `->` is confusing me... Could OP post the code of that, please? – SingerOfTheFall Jul 10 '12 at 14:06
  • 4
    You need to debug at the machine code (assembly language) level in order to find out exactly where and how the return value changes. I can think of 2 possibilities: (1) it doesn't, really, and (2) there is a mismatch of calling conventions involved. – Cheers and hth. - Alf Jul 10 '12 at 14:07
  • @SingerOfTheFall he's explicitly calling the -> operator, which takes no arguments. It's a valid way of dereferencing an iterator (though I've never ever seen it done this way) – Wug Jul 10 '12 at 14:07
  • @SingerOfTheFall: the code for `operator->()` will be inside `std::vector::iterator` (or whatever container his list uses). – Ben Voigt Jul 10 '12 at 14:07
  • @Wug, ok, think I got it now... – SingerOfTheFall Jul 10 '12 at 14:08
  • What compiler options are you using (especially optimization)? – Ben Voigt Jul 10 '12 at 14:09
  • 5
    If you have a vector storing objects, not pointers, you should definitely not be returning pointers to those objects unless you are really, really sure there will not be any reallocations in that vector. – Joris Timmermans Jul 10 '12 at 14:20
  • For all those who are suspecting the `operator->()` to be the problem, it isn't. I changed it to more clean code: `&(*it)` and I'm still having the same behavior. Which is normal, because that would explain why the return value would suddenly change... – Martijn Courteaux Jul 10 '12 at 14:21
  • @MadKeithV: Indeed, very fair point. But this still doesn't explain why the value before and after returning is different. Thanks for this, you are right! Do I have to fix this since the vector isn't being edited anymore at that point (i.e.: no more object pushes or pops, erases)? – Martijn Courteaux Jul 10 '12 at 14:23
  • @MartijnCourteaux - if the vector is truly const from that point onwards then the objects will remain where they are, and the pointers will remain valid. Even in that case though it may be a more canonical "C++-ish" approach to store iterators to the objects, not pointers. That way everyone looking at the code will immediately wonder about iterator invalidation, and in this case that's a good thing. – Joris Timmermans Jul 10 '12 at 14:27
  • @Cheersandhth.-Alf: Can you explain what you mean by saying '*mismatch of calling conventions*'? – Martijn Courteaux Jul 10 '12 at 14:37
  • 2
    You're using the debug/non-optimized build right? In otimized builds gdb will get confused about what's executing and not always show the right pointers/data. – Mark B Jul 10 '12 at 14:38
  • Could it be that the variable is never initialized ? Ie, does `getStructure` really returns `null` if the loop ends without anything ? It would not be the first time that I see someone being misguided by the behavior of the debugger and think that a statement is executed while it is not. – Matthieu M. Jul 10 '12 at 14:40
  • @MarkB: It is the non-optimized build (i.e.: Debug). By looking into XCode I found that the debugger was set to LLDB, and not GDB. I found this on LLDB: http://stackoverflow.com/a/9709462/155137 I'm going to debug the same with GDB. – Martijn Courteaux Jul 10 '12 at 14:44
  • When I use GDB, it is behaving correctly. So I guess it is a bug in LLDB. I'm going to print out the address of the pointer with a printf statement to see if LLDB is really wrong. – Martijn Courteaux Jul 10 '12 at 14:47
  • 1
    @MartijnCourteaux: a calling convention tells the compiler what the machine code expects and produces. e.g. arguments on stack or in registers, and the details of that. i don't know the calling conventions on the mac, sorry. but e.g. in Windows land there are two ways to return a result at the machine code level, namely (1) place it in memory specified by an implicitly passed pointer (used for RVO), or (2) pass it back in EAX register. if function uses different convention from caller expections, then ungood result. – Cheers and hth. - Alf Jul 10 '12 at 14:52
  • @Cheersandhth.-Alf: Thanks for the explanation. That makes sense. But this is very unlikely since it is compiled by the only one compiler (i.e.: the one shipped with XCode, provided Apple, which is G++, I think). – Martijn Courteaux Jul 10 '12 at 15:03

2 Answers2

2

It looked like the debugger I was using (LLDB and not GDB as I though first) did not display the memory values correctly. After switching to GDB, the debugger showed the memory addresses I excepted to see. When I added a print statement like this:

printf("StructureDef* pointer = %llx\n", (unsigned long long int)(void*) sDef);

it looks like GDB is right. For testing purposes, I switched back to LLDB, and magically it was working correctly as well.

Maybe, I'm dealing with what this guy wrote: https://stackoverflow.com/a/9709462/155137

I hope it is a bug in the LLDB debugger. Because, otherwise, I think there is something really broken on my installation.

Community
  • 1
  • 1
Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
0

Change the vector from std::vector<StructureDef> to std::vector<StructureDef*>. When you populate the vector with its constituent pointers to StructureDef, dynamically allocate memory for each one on the heap.

rurouniwallace
  • 2,027
  • 6
  • 25
  • 47
  • I know, that is already discussed in the comments. Take a look at this comment and the next ones: http://stackoverflow.com/questions/11414880/c-strange-behavior-return-value-changes-on-the-return-statement/11416260#comment15054980_11414880 – Martijn Courteaux Jul 10 '12 at 15:09
  • `boost::shared_array` would be better. – Roddy Jul 10 '12 at 15:10
  • No, not really, because it is like 500 lines of code, parsing an XML file with complex relation between different structs. But I can tell you that the vector is left unedited when the XML file loading is done. The `getStructure` method is called afterwars. – Martijn Courteaux Jul 10 '12 at 15:12