この offsetof() の実装が機能するのはなぜですか?

上記のコードでは、逆参照されている箇所はありません。 * の場合、逆参照が発生します。 または -> アドレス値で参照値を見つけるために使用されます。 * の唯一の用途 上記は、キャストを目的とした型宣言です。

-> 演算子は上で使用されていますが、値へのアクセスには使用されていません。代わりに、値のアドレスを取得するために使用されます。これはマクロ以外のコードのサンプルで、もう少しわかりやすくする必要があります

SomeType *pSomeType = GetTheValue();
int* pMember = &(pSomeType->SomeIntMember);

2 行目は、実際には逆参照を引き起こしません (実装に依存します)。 SomeIntMember のアドレスを返すだけです。 pSomeType

ご覧のとおり、任意の型と char ポインターの間で多くのキャストが行われています。 char の理由は、C89 標準で明示的なサイズを持つ唯一の (おそらく唯一の) 型の 1 つだからです。サイズは 1 です。サイズが 1 であることを確認することにより、上記のコードは、値の実際のオフセットを計算するという邪悪な魔法を実行できます。


offsetof の典型的な実装ですが 、それは標準によって義務付けられていません:

P J Plauger の「The Standard C Library」を読んで、それと <stddef.h> のその他の項目について説明してください。 これらはすべて、適切な言語に含めることができる (すべき?) 境界線上の機能であり、特別なコンパイラ サポートが必要になる可能性があります。

歴史的な関心事だけですが、私は 386/IX で初期の ANSI C コンパイラを使用しました (1990 年頃の歴史的な関心事についてお話しました) を使用し、そのバージョンの offsetof でクラッシュしました。 しかし、次のように修正すると機能しました:

#define offsetof(st, m) ((size_t)((char *)&((st *)(1024))->m - (char *)1024))

これはある種のコンパイラ バグでした。特に、ヘッダーがコンパイラと共に配布され、機能しなかったためです。


ANSI C では、offsetof そのように定義されていません。そのように定義されていない理由の 1 つは、環境によっては実際に null ポインター例外をスローしたり、他の方法でクラッシュしたりするためです。したがって、ANSI C は offsetof( ) の実装を残しています。 コンパイラビルダーに開かれています。

上記のコードは、NULL ポインターをアクティブにチェックせず、バイトが NULL ポインターから読み取られた場合にのみ失敗するコンパイラ/環境の典型です。