次のようなものがあるとします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 );
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
警告を抑制する 1 つの方法は、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) )
を介してキャストするため、警告は消えuintptr_t
ます。
問題は、これは C で必要なものを取得するための最良の (最も移植性の高い) 方法ですか? 具体的には:
struct
(同じ型の) 異なるメンバーで動作する関数を作成する機能。- によって返された値を使用
offsetof
して実際のメンバーへのポインターに変換する移植可能な方法。