Find The Offset Of An Element In A Structure In C- offsetof()

Subscribe To Our Feed | Follow Us On Twitter | Get Updates on Email

First of all, I apologize for the decreased frequency of updates. We have been quite busy with our offline lives and primary livelihoods lately keeping us away from posting much. But we intend to not let it remain like this for much longer. I’m posting a short article today about something that almost everyone of us has had to do at some point of time, i.e., to find the offset (or relative position in bytes) of an element in a structure. Let’s take the following structure as an example:

struct
{
  char a;
  int b;
  char c;
}example;

Now, if I were to ask you to find out the element b’s offset in the above structure, you won’t probably be able to answer with complete confidence unless I tell you the compiler you are working with and whether packing has been turned on or not. The easiest way to find it out is to use a small snippet of code to do it for us and that always works. e.g.

struct example s1;
unsigned int offset;
offset = (unsigned int)&s1.b - (unsigned int)&s1;

The above snippet will work, but not always (Hint). Many people use a much simplified form, which does not involve any pointer arithmetic:

unsigned int offset;
offset = (unsigned int)(&(((example *)(0))->b));

The above code is much simpler/faster but again, it might not be portable. So, what is the best method to do this portably. It’s quite simple really, just use the “offsetof” macro provided by any ANSI-C compliant compiler. It is present in stddef.h and can be used in the following way:

size_t offset;
offset = offsetof(example, b);

If you noticed, offsetof() also presents another advantage to you like the 2nd method, i.e., it does not require an extra structure to be defined. In fact, this macro is defined in forms similar to our method 2 but the benefit is that it ensures portability for your code.

© Safer Code | Find The Offset Of An Element In A Structure In C- offsetof()

Liked this post? Get FREE Updates
Subscribe to RSS feed

Or
Enter Your E-mail ID below

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • StumbleUpon
  • Reddit
  • Print this article!

Related posts

Tags: , , , , , ,

9 Comments

  • markandey says:

    does & operator work on a constant value?

  • 0 in itself is not a valid lvalue. But when you typecast it to the structure pointer type and then dereference an element from it, it becomes a valid lvalue and & operator can be used for it.

  • Zombie No. 5 says:

    Seriously guys…is this blog a complete joke or what? You’re casting address to an unsigned int to calculate the difference between two addresses.

    “Making Your Code Faster, Stronger, Safer…”

    How about learning some basic knowledge of C first?

  • @Zombie: Care to tell what exactly is the issue with the statement?

  • Santiago says:

    I was aware of the ‘offsetof’ macro, but for some gdb scripts I was forced to use the first implementation. However, it is a little cumbersome because you are forced to declare an instance of the struct type, for example a dummy global variable just to be easily accessible from gdb. Obviously, that can be a problem and is not very elegant.

    So I find the second implementation really elegant and useful, because it doesn’t need declaring an instance of the variable. I’ll probably change those gdb scripts, thanks!

    PS: IMHO the first implementation is more problematic than required, when doing pointer arithmetic is more adequate to use a pointer data type than integer types. Just operate with temp variables of type ‘void *’, and finally assign the result to an unsigned int. Casting first the addresses to unsigned int is really dangerous, in many cases the integer types are narrower than the address types.

    Cheers

  • Zombie No. 5 says:

    Shantanu Goel: First, you code doesn’t make sense. Apparently you meant “struct example {…};” not “struct {…} example;” or maybe you meant “typedef struct {…} example” but then you’re first example is wrong again.

    What a C programmer needs is discipline, discipline and discipline. Therefore a sentence like “The above snippet will work, but not always” is unworthy. It will NOT always work, period. Since there are completely portable and efficient ways to achieve what you want, there’s absolutely no point to suggest use of such nonsense code. You don’t teach people things by showing them how to do something wrong but how to do it right. The human brain has only limited capacity.

    What is this int nonsense anyway? Any half-decent C programmer would at least have used “unsigned long”. A more educated progammer would have used “size_t” or even “uintptr_t”. And a good developer just uses offsetof() because it’s part of the standard.

    This article would have been much more useful, if you had actually told the reader something about struct internal padding, alignment, order of elements etc. Otherwise, those readers for who any of this is news anyway, might wonder why you just don’t use sizeof(example.b) as offset. The point is, it isn’t just because that requires knowledge of the order of members, there are other reasons as well.

    Santiago: You must be using GCC or some other forgiving C compiler because
    ((void *)x – (void *)y) as you suggest is not ‘C’ since pointer arithmetic is not legal because “void” has no size. A correct variant is casting to “(char *)”.

  • Santiago says:

    Zombie: yep, you are right of course. I sometimes forget that ‘void *’ cannot be used for pointer arithmetic because I always try to avoid using it altogether unless completely needed (being a disciplined programmer involves things like this ;-)

    So in this first implementation instead of using directly a pointer to char I prefer to use the uintptr_t as you suggest above (it is defined for C99, but the stdint.h header can even be easily written for non-C99 compilers), and a ptrdiff_t type for storing the offsets.

    Cheers

  • Zombie: I agree with you on the struct declaration part. I should have been more careful while writing the declaration.
    But an “unsigned long” is as bad as an “unsigned int”. There is no better option amongst these two. As you can see though, the final solution proposed, which is the crux of the article, does indeed use size_t.
    And that particular statement that you refer to, was written just to show what not to do when you are going for portability. At no point of time, I “suggested” that this code be used. It was always clear that the first two forms are used by people already but they are not portable. I disagree when you say that someone should be taught only what is the right thing to do. I believe it is also equally important to tell them what’s wrong and that is exactly because “Human brain has limited capacity”. So, even if you tell them all the things that are right, they might get confused when they face something that is wrong and can’t decide on their own which side it lies on. Probably I could have explained it a bit more why it isn’t portable, yes.
    Moreover, I did not mention about the struct internal padding, alignment, etc because it was not geared towards a user who was a complete novice to it. It was geared towards someone who was already in the know/using one of the first two approaches and to tell him that there is a better way.
    But I’m glad that we have a learned programmer like you who keeps us in check, points out our innumerous flaws and eggs us on to write better. Thanks a lot for your contribution.

  • Superb blog! I am loving it!! Will come back again – already subcribed to you feeds also, thank you for giving such a good time for me to read ;)

Leave a Reply