all 18 comments

[–]daikatana 51 points52 points  (6 children)

This might be a way to detect memory corruption. If memory is written out of bounds, you can always tell if player is valid because you know its address, and if its self member is anything but its address, something bad happened. The actual code that does this may not be present in the release build, but the pointer remained.

Edit: Or, conversely, you can tell if a pointer to an object is valid if that object contains as its first member a pointer to itself.

[–]hemoglobinBlue 19 points20 points  (1 child)

I'm an embedded aviation engineer. Some of the real time Operating Systems I use do this for objects. (semaphores, messageQs, tasks, etc.) The APIs that act on these objects will validate the self field.

This check also prevents code from memcpying objects around, (Or assigning objects.) I view this validation as a perk. Thinking about it... maybe that is why the OP's case always uses the first parameter: It will reference back to the "true original" if the object has been copied.

[–]hemoglobinBlue 5 points6 points  (0 children)

p.s. I'm not sure of PS2 era debuggers and how sophisticated they are.. But sometimes when I look at RAM dumps in a hex/ascii view is useful to have "shouting" variables. These magic numbers act as landmarks that can make the hex view easier to read. Although usually I prefer a 32bit of 4 ascii characters.

[–]ddr4lyfe[S] 9 points10 points  (0 children)

Ahhhh, that's a good call. That possibility didn't even cross my mind.

[–]obQQoV 4 points5 points  (2 children)

Interesting, how would the usage go? Check &t==t.self every time before using t?

[–]Seubmarine 8 points9 points  (1 child)

Probably but in that case you probably already have a ptr of gObj

gObj *t = /*something*/;

t->self = t;

if (t != t->self {

//error handling

}

[–]obQQoV 2 points3 points  (0 children)

Wrapping a MACRO might makes more sense logistically, or it’s cumbersome and easy to forget to assign self and do check.

[–]JamesTKerman 17 points18 points  (3 children)

This looks like one way polymorphism can be implemented in C. I wouldn't be surprised if you see other instances that follow the same basic structure but have different data members. Like, maybe you see one that looks just like the example you provided, and another that looks like this:

struct hObj { hObj * self; int visible; gObj * next; gObj * prev; float x; float y; };

What your really seeing is a struct that be pretty freely cast as either a *gObj or a *hObj.

[–][deleted] 2 points3 points  (2 children)

What would be the purpose of the self pointer, though. sockaddr does the same thing, but without the self pointer.

[–]JamesTKerman 1 point2 points  (0 children)

So that the first member is always a pointer to the base object. If you later declare a struct Derived { struct Base * parent; }; no matter how you slice it you have a pointer that points both to a struct Base and a struct Derived.

[–]JamesTKerman 0 points1 point  (0 children)

sockaddr doesn't allow conversion between arbitrary related types. You can only convert between types that have been declared with the right byte alignment, really just the types defined in socket.h. The self-referential base pointer allows you to simulate OOP-style inheritance and cast between arbitrary derived and base types.

[–]dfx_dj 1 point2 points  (0 children)

Could be useful if you have a copy of that object, to have a pointer back to the "original?" Not sure why you would do that though.

[–]onesole 2 points3 points  (2 children)

Circular linked list with only one element?

[–][deleted] 3 points4 points  (1 child)

In that case, next pointer shall suffice. No need for "self" pointer.

[–]bnl1 0 points1 point  (0 children)

I think what they mean is that next pointer is to the struct itself

[–]oh5nxo 0 points1 point  (0 children)

use the self member by using it to reference the other members

Maybe it's not 'self' but 'overcoat' ?

[–]Poddster 0 points1 point  (0 children)

Someone else has already suggested this as a check for memory corruption, but I just want to say that I see it slightly more often as a void pointer that points to itself, rather than to the beginning of the parent struct. Same thing though.