views:

343

answers:

5

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);
}
+4  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.

Nyan
+8  A: 
Norman Ramsey
real nice, explanation! thanks
Chintan
@Norman: you gave a nicely detailed explanation of an utterly wrong, non-standard and generally *bad* piece of code.
Eli Bendersky
@Eli: Good point. I've edited my answer.
Norman Ramsey
+5  A: 

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));
}
AndreyT
valdo
Nyan
Mike
@valdo: What `offsetof` is doing internally is none of our business. `offsetof` is a standard library component. It is only C in terms of interface, while its implementation is not C and/or not required to be C. In other words, in your C code you might not be allowed to do what if perfectly acceptable for `offsetof`. And there is a dereference of a null pointer there. Operator `->` is a dereference.
AndreyT
@Mike: The results of your trials mean nothing more than just that there is some platform on which it "works". This, of course, does not make it a legal solution in C.
AndreyT
+1  A: 

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 to struct s
  • cast to struct s*
  • return the resultant ptr
Prabhu Jayaraman
A: 

The question has solved already. I just want to add another very similar and common interview question:

Describe the use for following macro:

#define X(y, z) ((int) &(((y *) 0)->z))

- Answer: to get the offset of the member z within a struct/class y

snowgoose