35

I think it is not possible in C but ask to verify this. Is it possible to make arithmetic between structure members without real variable of this type? For example:

typedef struct _s1
{
  int a;
  int b;
  int c;
} T1;

I want to see the offset of "c" member compared to structure beginning. It is easy if I have variable:

T1 str;

int dist = (int)&str.c - (int)&str;

But my structure is too big and it has no member in RAM (only in EEPROM). And I want to make some address calculations but not to define RAM member. I can do the job with structure pointer instead of structure variable (it will cost only 4 bytes) but the case is interesting for me.

i486
  • 6,491
  • 4
  • 24
  • 41
  • 8
    Define fake pointer and see? – Cthulhu Jan 30 '14 at 11:38
  • 3
    This has probably been answered here and discussed elsewhere a zillion times. I guess the answer should have been a Google search away. Did you Google this? – meaning-matters Jan 30 '14 at 11:41
  • If your struct is really that big, then casting its address, and the address of one of its members to `int` is likely to cause you grief anyway (pointers are 4/8 bytes, an `int` only has to be 2 bytes in size, but most implementations use 4 bytes) – Elias Van Ootegem Jan 30 '14 at 12:17
  • 3
    @EliasVanOotegem Considering the mention of EEPROM, there's a good chance this is running on an embedded system. For all we know, it might be a 8-bit or 16-bit system. – Bob Jan 30 '14 at 14:04
  • @Bob, fair point... but the remark on the pointer taking up 4 bytes, to me, suggests otherwise. Ah well, as long as the OP knows, and got the answer he was looking for – Elias Van Ootegem Jan 30 '14 at 14:48
  • Simple calculation and print of sizeof(int) and sizeof(void*) will quickly tell OP how big these values are for their architecture (and compiler settings)... – ChuckCottrill Jan 30 '14 at 16:58
  • 4
    Aside: your sample code should probably be `(char*)&str.c - (char*)&str`. I don't have a reference handy, but I don't think you're actually guaranteed that casting pointers to `int` and then doing arithmetic gives correct results. (also, you should use `intptr_t` or `uintptr_t` if you're casting pointers to integers) –  Jan 30 '14 at 18:54
  • 1
    @meaning-matters If you think that, then find where it's been answered and vote to close as a duplicate. – asteri Jan 31 '14 at 20:50

6 Answers6

56

Using offsetof you can make calculations between members without having to get hold of a variable of that type. All you need is the type itself and the name of the member.

A note why plain calculations are likely not to work out: data alignment. You do not know the amount of padding your compiler is going to throw at your structure and this can lead to very subtle mistakes or make seemingly correct calculations wrong if you change the structure.

pmr
  • 58,701
  • 10
  • 113
  • 156
24

First off, you're casting the individual addresses to ints. I don't think that's such a good idea. an int is guaranteed to be at least 2 bytes or more in size, an address/pointer is generally 4 or 8 bytes. Casting these to int just doesn't sit right. If I were to cast them to anything, I'd probably use unsigned long for 32 bit systems, and unsigned long long for 64 bit. I'd use the latter to be safe.
That said, I wouldn't cast the addresses at all, and just use the offsetof macro.

Add #include <stddef.h> to your file, and use the offsetof macro, which casts the offset value to size_t type, not an int, mind you. It works simply by using 0 as the memory address of the struct, and then returns the address of the given member, giving the actual offset:

size_t offset = offsetoff(T1, c);
                    //struct, member

As Zack pointed out in his comment, the next part of the answer is somewhat irrelevant, but for the links it contains, and for completeness' sake, I'll just leave it here - as offsetof is required of all implementations:

Define the macro yourself, if you don't have stddef.h for some reason (which it ought to, because offsetof has been parto fo the standard for some time now: C89 and up), like so:

#ifndef offsetof
#define offsetof(s, m) ((size_t)&(((s *) 0)->m))
#endif

That ought to do the trick, but be wary: this implementation may cause undefined behaviour. How and why is explained here
I've taken the offsetof definition above from the stddef.h source I found here, so you may just download that file, and use it, instead of defining the macro in your own file but keep in mind that the offsetof you use is not 100% reliable.

Anyway, that's enough for now, more info on Google, but here's a couple of links:

Community
  • 1
  • 1
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • 6
    Defining `offsetof` is a little bit dangerous. The implementation you show is a borderline case of undefined behavior (I can dig up a reference for this), but its OK to do that for an implementer because they know (or have to make sure) how the compiler behaves. – pmr Jan 30 '14 at 11:52
  • 1
    @pmr: Added some links on that to my answer, thx for pointing it out to me. I didn't know about `offsetof`'s quirks (my main source of C "knowledge" being K&R, which doesn't mention the macro at all). anyway, +1 to your answer for pointing out data alignment to the OP – Elias Van Ootegem Jan 30 '14 at 12:06
  • 2
    There's no excuse for an implementation failing to provide `stddef.h`, with `offsetof` in it, nowadays - it's been required of all implementations, freestanding or otherwise, since C89. – zwol Jan 30 '14 at 16:13
15

You can use offsetof defined in stddef.h.

I know this goes beyond what you asked, but there is one interesting use of offsetof used for example in the linux kernel code, the container_of macro. container_of can return you the address of the parent structure, given a pointer to one of its members:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

That you can use for example:

// pointer_to_c points to a member of the struct, c in this case
// and you want to get the address of the container
T1 *container;
container = container_of(pointer_to_c, T1, c);
fede1024
  • 3,099
  • 18
  • 23
  • I like the `container_of` macro, but the `(char *)` cast looks a bit _legacy_ to me, if you don't mind my saying. I think nowadays, we'd use a void pointer – Elias Van Ootegem Jan 30 '14 at 11:54
  • 2
    @EliasVanOotegem i think the (char *) is necessary because later __mptr used for pointer arithmetic. It needs to be decremented exactly by offsetof(...) bytes. You should never use arithmetic on viud pointers http://stackoverflow.com/questions/3523145/pointer-arithmetic-for-void-pointer-in-c – fede1024 Jan 30 '14 at 12:01
  • 1
    Good point... I just saw `(char *)__mptr `, didn't stop to consider the implications of `void * - size_t` :) – Elias Van Ootegem Jan 30 '14 at 12:09
11

stddef.h has a macro called offsetof which gives the offset of a member from the beginning of a structure.

size_t t = offsetof( T1 , c ) ;

This way you don't have to actually declare any struct variables.

Harikrishnan
  • 9,688
  • 11
  • 84
  • 127
this
  • 5,229
  • 1
  • 22
  • 51
9

Take a look to Data structure alignment

That's correct:

size_t dist = offsetof(struct _s1, c);
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
7

In the header stddef.h there is a macro; offsetof. You can use it on your struct like this:

int dist = (int)offsetof(T1, c);
Anthony
  • 12,177
  • 9
  • 69
  • 105