ア | イ | ウ | エ | オ |
カ | キ | ク | ケ | コ |
サ | シ | ス | セ | ソ |
タ | チ | ツ | テ | ト |
ナ | ニ | ヌ | ネ | ノ |
ハ | ヒ | フ | ヘ | ホ |
マ | ミ | ム | メ | モ |
ヤ | ユ | ヨ | ||
ラ | リ | ル | レ | ロ |
ワ | ヰ | ヴ | ヱ | ヲ |
ン |
A | B | C | D | E |
F | G | H | I | J |
K | L | M | N | O |
P | Q | R | S | T |
U | V | W | X | Y |
Z | 数字 | 記号 |
C/C++で、構造体内にあるメンバーのオフセットを返す機能。多くの場合マクロとして実装される。
#include <stddef.h> size_t offsetof(type, member);
#include <cstddef> size_t offsetof(type, member);
構造体は、仕様変更等でメンバーの追加や削除が発生することがある。
そうでなくても、コンパイラーによっては、環境にあわせてフィールド間にパディングを挿入したりフィールドの順序を入れ替えたりすることもあるため、メモリー空間への配置については実装により変動する。
このような現状から、フィールドのオフセットを定数として決め打ちすることは危険である。オフセットが必要な状況においては、このoffsetof機能は有用である。
なお、member がビットフィールドの場合は使用できず、コンパイル時点でエラーとなる。
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カーネルでは、次のようにして使う。
#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では、STDDEF.H で次のように定義されている。
#define offsetof(s,m) (size_t)&(((s *)0)->m)
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 でキャストして整数になって返却される。
コメントなどを投稿するフォームは、日本語対応時のみ表示されます