2

Suppose I have a struct like:

typedef struct S S;
struct S {
    S *next;
    // ...
    S *gc_next;
};

I.e., it contains multiple "next" pointers to be simultaneously on multiple linked lists. If I want to write a single function that can follow any "next" pointer, I can pass the offset of which "next" pointer I want, e.g.:

void S_free_impl( S *p, size_t next_offset ) {
    while ( p ) {
        S *next = *PTR_OFFSET( p, S*, next_offset );
        free( p );
        p = next;
    }
}

where PTR_OFFSET is a macro that, given a pointer to some struct, the type of the member I want (in this case, S*), and the offset of the member I want, then dereferencing that pointer, I can get the value of the wanted member.

To get the offset, I can write a front-end macro to call S_free_impl() that uses offsetof:

#define S_FREE(PTR,NEXT)         S_free_impl( (PTR), offsetof( S, NEXT ) )

then use it like:

S_FREE( p, gc_next );            // free nodes following "gc_next" pointer

An initial implementation of the PTR_OFFSET macro can be:

#define PTR_OFFSET(PTR,TYPE,OFFSET) \
    (TYPE*)((char*)(PTR) + (OFFSET))

However, if you compile, say, using gcc -Wcast-align, you get:

foo.c:21:24: warning: cast from 'char *' to 'S **' (aka 'struct S **') increases
      required alignment from 1 to 8 [-Wcast-align]
            S *next = *PTR_OFFSET( p, S*, next_offset );
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

One way to suppress the warning, is to write a sort-of C equivalent of C++'s reinterpret_cast:

#define REINTERPRET_CAST(T,EXPR)  ((T)(uintptr_t)(EXPR))

then rewrite PTR_OFFSET to use it:

#define PTR_OFFSET(PTR,TYPE,OFFSET) \
    REINTERPRET_CAST( TYPE*, REINTERPRET_CAST( char*, (PTR) ) + (OFFSET) )

then the warning goes away because of casting through a uintptr_t.


The question is: is this the best (most portable) way to get what I want in C? Specifically:

  1. The ability to write functions that operate on different struct members (of the same type).
  2. A portable way to use the value returned by offsetof to convert it to an actual pointer-to-member.
Paul J. Lucas
  • 6,895
  • 6
  • 44
  • 88

0 Answers0