0

次のようなものがあるとします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 で必要なものを取得するための最良の (最も移植性の高い) 方法ですか? 具体的には:

  1. struct(同じ型の) 異なるメンバーで動作する関数を作成する機能。
  2. によって返された値を使用offsetofして実際のメンバーへのポインターに変換する移植可能な方法。
4

0 に答える 0