Here is an interview question that I saw on some forum. I've been trying to figure out how it works but I don't quite get it. Could somebody explain how it works?
Q: Given a pointer to member a within a struct, write a routine that returns a pointer to the struct.
struct s
{
...
int a;
…
};
struct s *get_s_ptr(int *a_ptr)
{
// implement this.
}
The answer is:
struct s* get_s_ptr(int *a_ptr)
{
return (struct s*)((char*)a_ptr - (int)&((struct s*)0)->a);
}
How does it work?
The fundamental equation here (all arithmetic in bytes) is
address of struct member s->a == s + byte offset of a
Given the type of s
, a single compiler, and a single target machine, they determined the byte offset of a
—it's the same for every struct of type s.
You're given the left-hand side and your interviewer asked you to recover s
. You can do this by getting a new equation; subtract the byte offset from both sides:
address of struct member s->a - byte offset of a == s
In the problem, you're given the address of s->a
, but you have to figure out the byte offset. To do this you use the original equation again with s
set to zero:
address of struct member s->a where s is zero == zero + byte offset of a
== byte offset of a
The left-hand side in C is built as follows
struct pointer s where s is zero (struct s *)0
struct member s->a where s is zero ((struct s*)0)->a
address of s->a where s is zero &((struct s*)0)->a
Final steps:
a_ptr
is cast to char *
.struct s *
.Addendum: As Eli Bendersky points out, you should try to avoid situations where this code would be necessary. There is almost always a better way.
The answer is: it doesn't. It doesn't work, even if it might seem to "work" at the first sight. The "answer" makes an attempt to dereference a null pointer, which leads to undefined behavior. So, unless your idea of "working" includes undefined behavior, that answer does not work.
There are more problems with that solution, besides the attempt to derefercence a null pointer (although that alone is perfectly enough to throw that "answer" to garbage bin). Another problem is that the result of (struct s*) 0
is a null pointer of struct s *
type. The language makes no guarantees about the actual physical value of a null pointer. If could easily be something like 0xBAADFOOD
, which would immediately screw up the intended functionality of the "answer".
The proper implementation of the implied technique would involve the standard offsetof
macro (already suggested in Nyan's answer, but I'll repeat it one more time)
struct s* get_s_ptr(int *a_ptr)
{
return (struct s*) ((char *) a_ptr - offsetof(struct s, a));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With