Given a pointer to member a within a struct, write a routine that returns a pointer to the struct
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;
…
};
stru开发者_开发问答ct 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:
- To make the arithmetic legal C this byte offset is cast to an integer.
- To make sure the subtraction is done in units of bytes,
a_ptr
is cast tochar *
. - To give the result the right type the difference is cast to
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));
}
You can use offsetof macro.
struct s* get_s_ptr(int *a_ptr)
{
return (struct s*)((char*)a_ptr - offsetof(struct s,a) );
}
I's late. my internet connection's slow.
Thought this would be helpful,
/* offsetof example */
#include <stdio.h>
#include <stddef.h>
struct mystruct {
char singlechar;
char arraymember[10];
char anotherchar;
};
int main ()
{
printf ("offsetof(mystruct,singlechar) is %d\n",offsetof(mystruct,singlechar));
printf ("offsetof(mystruct,arraymember) is %d\n",offsetof(mystruct,arraymember));
printf ("offsetof(mystruct,anotherchar) is %d\n",offsetof(mystruct,anotherchar));
return 0;
}
Output:
offsetof(mystruct,singlechar) is 0
offsetof(mystruct,arraymember) is 1
offsetof(mystruct,anotherchar) is 11
So in your case,
return (struct s*) ((char *) a_ptr - offsetof(struct s, a));
- cast
aptr
to char* - subtract the offset of
a
wrt tostruct s
- cast to
struct s*
- return the
resultant ptr
精彩评论