2

Consider the following code fragment:

struct data_t {
    int data1;
    int data2;
    struct data_t *next;
    size_t size;
    int data3;
    int data4;
};

int *ptr;
struct data_t data;

...

ptr = &data.data4;

Now using pointer, which is set to point to the last element in the structure, how can one use that pointer to access the first element in the structure (data1)?

Normally, what I would do in this case is back up the pointer by so many words to point to that element, but there is a problem. The pointer variable next in the middle of the structure has a varying size depending on the platform. If this is running on a 32-bit platform, then the pointer is 4 bytes while on a 64-bit platform, the pointer takes up 8 bytes. A similar issue happens with the size_t datatype as well.

Although not clear in the example, the structure is the header to a block of memory that is variable in size and is part of a linked list. AKA a free list in a memory allocator. Other than using some kind of an initialization that calculates the size of the pointer itself, is there a portable way of getting the address of the first element of the structure?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Daniel Rudy
  • 1,411
  • 12
  • 23

1 Answers1

5

You can use offsetof to know how far a member is from the start of the structure. In this case:

struct data_t *p = (struct data_t *)( (char *)ptr - offsetof(struct data_t, data4) );

Obviously this requires you to know that the pointer is pointing at a data4 already, there's no way to autodetect that or anything. And, of course, it would be preferable to use a code design where you pass around the struct data * in the first place.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    Note: considering code readability you probably want to do this with a macro. – Karoly Horvath Mar 13 '16 at 22:32
  • 1
    Perhaps: `#define structure_base(structure_type, member_name, member_address) ((structure_type *)((char *)(member_address) - offsetof(structure_type, member_name)))` or thereabouts — maybe a little less verbosity in the argument names. – Jonathan Leffler Mar 14 '16 at 00:00
  • Unfortunately, when I tried this, the compiler complained that this was an extension. I'm using CLang. So, it looks like that I will have to do this the other way. +1 for teaching me something new. – Daniel Rudy Mar 14 '16 at 07:06
  • @DanielRudy my code is part of Standard C and I would expect any compiler to support it. Perhaps what you actually tried compiling was not exactly the same as I wrote. – M.M Mar 14 '16 at 07:31
  • It helps if I read the error message correctly. Because of the complexity of the datatypes that I am working with, I have nested structs and unions. The error message said that using extended field designator is an extension. There is another warning about pointer alignment due to the char * in your code. Since I am running only with 32-bit or 64-bit values, I changed it to the following: `struct data_t *p = (struct data_t *)ptr - (offsetof(struct data_t, data4) / sizeof(int)));` This does work, I tested it on both a 32-bit and 64-bit platform. – Daniel Rudy Mar 16 '16 at 10:53
  • @DanielRudy if you only ever use my code on a real `data4` then there is no alignment issue. (A warning suggests you used it on some other member). Also there is no extended field designator in my code. Your alternative causes undefined behaviour because `ptr` may be incorrectly aligned for `struct data_t`, also the offset is wrong except for some special cases. If you post your real struct definition then I will post a version that works with your code. – M.M Mar 16 '16 at 11:24
  • Thank you for your offer, but I have a version that I will use. As I said, I tested it and it is working, so I will go with what I have. Thank you again though. – Daniel Rudy Mar 17 '16 at 09:23