offsetof

読み:オフセット・オブ
外語:offsetof 英語
品詞:名詞

C/C++で、構造体内にあるメンバーのオフセットを返す機能。多くの場合マクロとして実装される。

目次

C

#include <stddef.h>
size_t offsetof(type, member);

C++

#include <cstddef>
size_t offsetof(type, member);

offsetof() は、構造体 type 中にあるフィールド member の、構造体先頭からのオフセットを size_t 型で返す。

オフセットはポインター差分に相当すると思われるが、ptrdiff_t 型などではなく、なぜか size_t 型で返す仕様となっている。

構造体は、仕様変更等でメンバーの追加や削除が発生することがある。

そうでなくても、コンパイラーによっては、環境にあわせてフィールド間にパディングを挿入したりフィールドの順序を入れ替えたりすることもあるため、メモリー空間への配置については実装により変動する。

このような現状から、フィールドのオフセットを定数として決め打ちすることは危険である。オフセットが必要な状況においては、このoffsetof機能は有用である。

なお、member がビットフィールドの場合は使用できず、コンパイル時点でエラーとなる。

gcc 4.4

Linuxのユーザー空間で、gcc 4.4の場合は、次のように定義されている。

/usr/lib/gcc/x86_64-linux-gnu/4.4/include/stddef.h

#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)

GCC 3.5から追加された独自の組み込み関数 __builtin_offsetof() を用いている。

Linux

Linuxカーネルでは、次のようにして使う。

#incluce <linux/stddef.h>

定義は、/usr/src/linux-headers-2.6.XX-XX/include/linux/stddef.h などにある。

AndroidのLinuxカーネルでも external/kernel-headers/original/linux/stddef.h に存在し、offsetof の定義は全く同じ内容である。

#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

__compiler_offsetof()は、__builtin_offsetof()の別名である。

#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)

Visual C++ 6.0

Visual C++ 6.0では、STDDEF.H で次のように定義されている。

#define offsetof(s,m)   (size_t)&(((s *)0)->m)

Visual C++ 2013

Visual C++ 12.0では、stddef.h で次のように定義されている。

/* Define offsetof macro */
#ifdef __cplusplus

#ifdef _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) )
#else  /* _WIN64 */
#define offsetof(s,m)   (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
#endif  /* _WIN64 */

#else  /* __cplusplus */

#ifdef _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else  /* _WIN64 */
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif  /* _WIN64 */

#endif  /* __cplusplus */

さすがMicrosoftだけあって、(ptrdiff_t) でないとおかしいということに気づいた記述になっている。またC++でビルドすると、const volatile char&でreinterpret_castするようになっている。

オフセットを計算によって求める一般的な方法は、次の手法である。

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

なぜこれでオフセットが求まるのかを説明する。

オフセットとは基準からの差のことであり、求めたいメンバーのアドレスから、その構造体の先頭アドレスを引き算すればよいだけである。しかし、「その構造体の先頭アドレス」が0であれば、引き算が簡単である。そこで、このような難解な式に至った。

この式の実質的な処理は以下のみである。

(size_t) &((TYPE *)0)->MEMBER

そしてこの式は、いきなりメインイベントから始まる。

(size_t) &((TYPE *)0)->MEMBER

カッコの中のこの部分で、整数0を、入力された構造体名でキャストしてポインターとしている。これによって、このカッコ内には、アドレス0に配置された構造体のポインターが誕生する。

次に、& と -> では -> の方が優先順位が高いので、-> の演算が始まる。

(size_t) &((TYPE *)0)->MEMBER

この部分は、構造体が配置されるアドレスに関わらず、先の構造体中のメンバーが指し示される。

最後に、アドレス取得演算子の演算が始まる。

(size_t) &((TYPE *)0)->MEMBER

この演算子の対象範囲はここ。

(size_t) &((TYPE *)0)->MEMBER

この -> 演算子により、構造体中のメンバーのポインターを指し示す。

さてこの構造体だが、最初の定義によりアドレス0に配置されている。構造体のアドレスが今回は0と特別に分かっている。-> によって配置アドレスが判明するが、構造体の先頭アドレスが 0 なので、この内容は実質的にオフセットと等価となる。

(size_t) &((TYPE *)0)->MEMBER

最終的に、size_t でキャストして整数になって返却される。

用語の所属
Linux
関連する用語
連結リスト

コメントなどを投稿するフォームは、日本語対応時のみ表示されます


KisoDic通信用語の基礎知識検索システム WDIC Explorer Version 7.04a (27-May-2022)
Search System : Copyright © Mirai corporation
Dictionary : Copyright © WDIC Creators club