假设我有一个struct
喜欢:
typedef struct S S;
struct S {
S *next;
// ...
S *gc_next;
};
即,它包含多个“下一个”指针同时位于多个链表上。如果我想编写一个可以跟随任何“下一个”指针的函数,我可以传递我想要的“下一个”指针的偏移量,例如:
void S_free_impl( S *p, size_t next_offset ) {
while ( p ) {
S *next = *PTR_OFFSET( p, S*, next_offset );
free( p );
p = next;
}
}
wherePTR_OFFSET
是一个宏,给定一个指向 some 的指针struct
,我想要的成员的类型(在这种情况下,S*
),以及我想要的成员的偏移量,然后取消引用该指针,我可以获得想要的成员的值。
要获得偏移量,我可以编写一个前端宏来调用S_free_impl()
它offsetof
:
#define S_FREE(PTR,NEXT) S_free_impl( (PTR), offsetof( S, NEXT ) )
然后像这样使用它:
S_FREE( p, gc_next ); // free nodes following "gc_next" pointer
宏的初始实现PTR_OFFSET
可以是:
#define PTR_OFFSET(PTR,TYPE,OFFSET) \
(TYPE*)((char*)(PTR) + (OFFSET))
但是,如果你编译,比如说,使用gcc -Wcast-align
,你会得到:
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 );
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
抑制警告的一种方法是编写一种与 C++ 等效的 C reinterpret_cast
:
#define REINTERPRET_CAST(T,EXPR) ((T)(uintptr_t)(EXPR))
然后重写PTR_OFFSET
以使用它:
#define PTR_OFFSET(PTR,TYPE,OFFSET) \
REINTERPRET_CAST( TYPE*, REINTERPRET_CAST( char*, (PTR) ) + (OFFSET) )
然后警告消失,因为通过a投射uintptr_t
。
问题是:这是在 C 中获得我想要的东西的最佳(最便携)方式吗?具体来说:
struct
编写对不同成员(相同类型)进行操作的函数的能力。- 一种使用返回的值
offsetof
将其转换为实际指向成员的指针的可移植方式。