18

Linuxカーネルの二重にリンクされた循環リストの実装を見ていると、次のマクロが見つかりました。

#define container_of(ptr, type, member) ({           \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

これが機能する方法は、メンバーの1つのアドレスのみが指定された構造体へのポインターを返すことです。

struct blabla
{
    int value;
    struct list_head *list;
}

したがって、リストへのポインタのみを指定すると、blablaへのポインタを取得できます(そして「値」に到達できます)。私の質問に対して、これを可能な限りポータブルにするにはどうすればよいですか(C89 / C99に準拠したベストケース?)。typeof()を使用しているため、これはgccのみです。

これは私がこれまでに得たものです:

#define container_of(ptr, type, member) (                  \
                (type *) (char *)(ptr)-offsetof(type,member)\
                )

このスニペットはISO標準に準拠していますか(したがって、準拠しているコンパイラーでコンパイルできるはずです)?

4

4 に答える 4

20

Ouah がコメントしたように、({ ... })ステートメント式は GNU 拡張です。あなたはそれを使うことができなくなります。あなたの核となる式は必要なものに近いですが、括弧が十分ではありません:

#define container_of(ptr, type, member) \
                      ((type *) ((char *)(ptr) - offsetof(type, member)))

それは私にはきれいに見えます。SO の 2 行にまたがるだけです。

于 2012-04-22T16:28:10.003 に答える
15

マクロは、 で型チェックを実行するように記述されていptrます。__typeof__ステートメント式の代わりに複合リテラルを使用し、コンパイラが gcc と互換性がない場合は、使用する代わりにポインターの単純なチェックにフォールバックすることができます。

#ifdef __GNUC__
#define member_type(type, member) __typeof__ (((type *)0)->member)
#else
#define member_type(type, member) const void
#endif

#define container_of(ptr, type, member) ((type *)( \
    (char *)(member_type(type, member) *){ ptr } - offsetof(type, member)))
于 2012-04-22T16:48:23.640 に答える
3

タイプチェック付きのISO C90互換バージョン。(ただし、注意:の 2 つの評価ptr!)

#define container_of(ptr, type, member) \
   ((type *) ((char *) (ptr) - offsetof(type, member) + \
              (&((type *) 0)->member == (ptr)) * 0))

struct container {
  int dummy;
  int memb;
};


#include <stddef.h>
#include <stdio.h>

int main()
{
  struct container c;
  int *p = &c.memb;
  double *q = (double *) p;
  struct container *pc = container_of(p, struct container, memb);
  struct container *qc = container_of(q, struct container, memb);
  return 0;
}

テスト:

$ gcc -Wall containerof.c
containerof.c: In function ‘main’:
containerof.c:20:26: warning: comparison of distinct pointer types lacks a cast
containerof.c:20:21: warning: unused variable ‘qc’
containerof.c:19:21: warning: unused variable ‘pc’

26の場合はdistinct pointer types警告が表示されますが、25 の場合は表示されません。これは、ポインターの誤用に関する診断です。

私は最初に型チェックをコンマ演算子の左側に配置しようとしましたが、gcc はそれが効果がないことについて不平を言います。これは迷惑です。しかし、それをオペランドにすることで、それが確実に使用されるようにします。

この&((type *) 0)->memberトリックは ISO C では明確に定義されていませんが、定義に広く使用されていoffsetofます。コンパイラが に対してこのヌル ポインタ トリックを使用する場合、offsetof独自のマクロでほぼ確実に動作します。

于 2012-04-24T02:12:35.540 に答える