| rollie (304) | ||||
I was looking at a problem on Stack Overflow, and came across the following:
with results (g++ 4.7):
I don't think I've come across this syntax; what are pa,pb,pc, and pt? It seems like the print function outputs the offset of each member variable from the beginning of A, but pa outputs 0x00000004, when 0x00000001 would be expected (given t is a char). Anyone have a reference to a good description of this functionality? | ||||
|
Last edited on
|
||||
| Cubbi (1583) | ||||
They are pointers to members
That is correct.
That is not correct: offset of a cannot be 1 because a is an int, which, on your and almost everyone's system, is aligned to 4 byte boundary (its memory address must be divisible by 4) | ||||
|
|
||||
| Disch (8348) | ||
I've never seen this syntax before either. But it looks like you are correct in your assessment of it outputting the offset of each member.
Structure padding. It's putting the int on the next 4-byte boundary. That is very typical. | ||
|
Last edited on
|
||
| rollie (304) | |
| Thanks guys - have a couple interviews coming up, trying to cram as much obscure knowledge as I can :) | |
|
|
|
| JLBorges (1336) | |||
OK, here's are a couple of interview questions:
1. If a particular implementation prints 0 for line 12, what could it print for line 13? How would this compiler evaluate the condition in the if statement on line 18? 2. If a particular implementation prints 0 for line 13, what could it print for line 12? How would it evaluate the dereference operator on line 19? | |||
|
Last edited on
|
|||
| BlackSheep (388) | |||
I'm pretty sure pointer-to-members with a null value are represented as -1, but your compiler tries to trick you:
My output: a_ptr = 1. A_null_ptr = 0. union_value = -1. Both union_ptr and A_null_ptr hold the nullptr, but only union_value prints the actual value because it's forced to print it as an int. Pointers-to-members are trippy. | |||
|
|
|||
| ne555 (4041) | ||
Edit: Learn COBOL then. | ||
|
Last edited on
|
||
| rollie (304) | |||
Still haven't seen a great article describing the rules of pointer-to-member, but it seems like they aren't implicitly castable to long, so I would have to assume the lines
are going to give misleading results. If the first gives 0 though, I would expect the next to give -1 (as BlackSheep said), but it seems this would be entirely compiler dependent. It could theoretically be implemented as '5'; the compiler would know that while values '3', and '4', represent the 4th and 5th data members, values 6, 7, etc would represent the 6th, 7th, etc data members. Or for your 2nd question, 'null' could be '0' when viewed as an integer, with 1, 2, etc representing the 1st, 2nd, etc member variables. The logic in foo() seems like it will work as expected regardless of the compiler. Whatever the conversion of nullptr to type int A::* results in during the execution of main, the result will be the same when evaluating the line if( pm != nullptr ) (which I believe could be simply written as if( pm ), given that conversion to bool does seem to be defined, and evaluates as 'true' even if the integral value is '0'). Likewise, regardless of the internal implementation of pointer-to-member, a.*pm += v ; seems like it would work in any case, as this is the proper syntax for this type of construct.
| |||
|
|
|||
| JLBorges (1336) | |||||||
> It could theoretically be implemented as '5'; the compiler would know that while values '3', and '4', represent the 4th and 5th data members ... No. Because this is well defined:
Far more interesting question: How could pointers to non-static member functions be implemented? Hint: If the pointer points to a virtual function, it behaves as expected - polymorphically. The same pointer may be null, or may point to either virtual or non-virtual functions.
| |||||||
|
Last edited on
|
|||||||
| Cubbi (1583) | ||
Would posting a link to the Fast Delegates webpage qualify as a spoiler? | ||
|
|
||
| rollie (304) | |||
|
Cubbi, I've read that in the past, definitely an interesting article, one I think I will review :) JLBorges, int A::*p0 = 0 ; is defined, but doesn't seem to set p0 to "0", it sets it to -1 (same as nullptr). If an implementation stored the nullptr (0) value as '5' in p0, it would be no different than storing it as -1, except that the dereference logic would have to be unnecessarily complicated/inefficient, such as
Wouldn't this still be a (silly) standards-compliant implementation, or am I not understanding correctly? Without using the fast delegates article, my recollection is something along the lines that for non-virtual functions, the address (or offset from A) is stored in the fp. For virtual functions, the index to lookup in the vtable, along with the offset required for the 'this' pointer to make it look like the appropriate type of object is stored. IIRC, this can be a single union. Some implementations (in the past?) had generated a thunk function to offset the this pointer and then call the appropriate function. I believe this would result in member function pointers being the same size as non-member function pointers, but you'd potentially have a lot of auto-defined functions. | |||
|
Last edited on
|
|||
| JLBorges (1336) | |||
|
> If an implementation stored the nullptr (0) value as '5' in p0, it would be no different than storing it as -1, > except that the dereference logic would have to be unnecessarily complicated/inefficient, such as --- > Wouldn't this still be a (silly) standards-compliant implementation Yes it would be. I stand corrected. Thanks.
> For virtual functions, the index to lookup in the vtable, along with ... The key point again being that the null pointer must be distinguishable from the pointer to member function with an offset of zero in the vtable. | |||
|
|
|||
| rollie (304) | |
|
As a follow up, the studying paid off in the end, so finally gainfully employed again :) Thanks all! I did have one amusing situation though, where an interviewer asked me: "If 2 binaries both link to the same shared library, are there 2 copies of the shared library in memory or just one used by both binaries?" I fairly confidently answers "2 copies, one for each binary". He explained I was wrong and that, in fact, only 1 copy ever exists in memory, and proceeded to use the rest of his interview time building on and asking questions about behavior related to this shared-in-memory library. I even started to doubt myself by the end...suffice it to say, I didn't take that job! | |
|
|
|
| JLBorges (1336) | |
|
> "If 2 binaries both link to the same shared library, > are there 2 copies of the shared library in memory or just one used by both binaries?" >> I fairly confidently answers "2 copies, one for each binary". I think the two of you used two different meanings of 'in memory' - in physical memory (pages in RAM/swap) or in virtual memory (virtual pages in a process address space). Typically there is only one object in physical memory for each non-writable section (code,read only data). If the implementation supports COW, initially there is only one object in physical memory for each writable section; but shadow objects are created on a per-process basis as writes into memory take place. | |
|
|
|
| rollie (304) | |
|
Hmm, maybe should have done more investigation. The stack overflow questions I saw seemed to indicate each process received its own copy - I suppose I was wrong in this case after all! http://www.linuxquestions.org/linux/articles/Technical/Understanding_memory_usage_on_Linux | |
|
|
|