通常PC用 / 人気 更新 今日 カテ |
電子計算機 > 仕様・構造 > ソフトウェア > プログラミング言語 > 言語仕様 > 予約語・関数など |
offsetof |
辞書:電算用語の基礎知識 プログラミング仕様編 (PTPROGS) |
読み:オフセット-オブ |
外語: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 でキャストして整数になって返却される。
リンク |
通信用語の基礎知識検索システム WDIC Explorer Ver 7.04a (27-May-2022) Search System : Copyright © Mirai corporation Dictionary : Copyright © WDIC Creators club |