2011-10-26 20 views
8

可能性の重複:
Why does this C code work?
How do you use offsetof() on a struct?C offsetofマクロはどのように機能しますか?

私は、インターネット上でこのoffsetofはマクロについて読むが、それはそれがために使用されているかを説明しません。

#define offsetof(a,b) ((int)(&(((a*)(0))->b))) 

これは何をしようとしていますか、それを使用する利点は何ですか?

+2

。彼らは 'int'ではなく' size_t'にキャストする必要があり、nullポインタ定数であっても、キャストする前に結果から '(char *)0'を減算するべきでしょう。 –

答えて

12

未定義の動作を呼び出すため(size_tではなく、intの間違ったタイプを使用する)、利点はなく、使用しないでください。

Cの標準のような、あなたが構造体の要素のオフセットを必要とする場合のために、実際に動作stddef.hoffsetofマクロを定義しています

あなたは、プログラムのフィールドを埋める聞かせ
#include <stddef.h> 

struct foo { 
    int a; 
    int b; 
    char *c; 
}; 

struct struct_desc { 
    const char *name; 
    int type; 
    size_t off; 
}; 

static const struct struct_desc foo_desc[] = { 
    { "a", INT, offsetof(struct foo, a) }, 
    { "b", INT, offsetof(struct foo, b) }, 
    { "c", CHARPTR, offsetof(struct foo, c) }, 
}; 

例えば、名称によってstruct foo JSONファイルを読むとき

+0

申し訳ありません - offsetofマクロは、特にC標準で定義されているので、未定義の動作をどのように引き起こしますか? –

+3

'stddef.h'の標準的な' offsetof'マクロはUBを起動しません。この方法でオフセットを計算するための独自のハックを定義すると、UBが呼び出されます。 –

+0

マクロの独自のバージョンを定義することで、定義されていない動作が発生するという標準的な言葉を引用してください。 –

4

structの特定のメンバーのバイトオフセットが見つかりました。たとえば、もしあなたが以下の構造を持っていた:

struct MyStruct 
{ 
    double d; 
    int i; 
    void *p; 
}; 

が次にあなたが offsetOf(MyStruct, d) == 0offsetOf(MyStruct, i) == 8、および offsetOf(MyStruct, p) == 12を持っていると思いますが(つまり、 dという名前のメンバーは、構造などを開始してから0バイトです)。

あなたの構造体のインスタンスがアドレス0(((a*)(0))の部分)に存在し、目的の構造体メンバのアドレスを取得して整数にキャストするというふうに動作します。アドレス0のオブジェクトの参照解除は通常エラーとなりますが、アドレスの演算子&とメンバー参照解除->が互いに打ち消し合うので、アドレスを取っても問題ありません。

通常、一般化されたシリアライズフレームワークに使用されます。何らかの種類のワイヤデータ(ファイルやネットワークのバイトなど)とメモリ内のデータ構造を変換するためのコードがある場合は、メンバ名からメンバオフセットへのマッピングを作成すると便利なことがよくあります。一般的な方法で値をデシリアライズします。

+0

質問はCですが、まだ 'struct MyStruct'を使用しなければなりません。 ;) –

24

R ..あなたの質問の2番目の部分に対する彼の答えは正しいです:このコードは最新のCコンパイラを使用するときには勧められません。

しかし、これは実際に何をしているのか、あなたの質問の最初の部分に答えるためには、次のとおりです。

(
    (int)(  // 4. 
    &((  // 3. 
     (a*)(0) // 1. 
    )->b)  // 2. 
) 
) 

が内側から出て働いて、これは...にアドレスをキャストこのbフィールド

  • のアドレスを取得する(不正配置)この構造体のフィールドb Structオブジェクト
  • を取得する構造体ポインタタイプa*
  • に値ゼロを鋳造

    概念的には、構造体オブジェクトをメモリアドレス0に配置してから、particulのアドレスarフィールドがあります。これにより、構造体の各フィールドのメモリ内のオフセットを把握できるので、構造体をバイト配列との間で変換するための独自のシリアライザとデシリアライザを記述することができます。

    実際にゼロポインタを逆参照すると、プログラムはクラッシュしますが、実際にはコンパイラですべてが発生し、実際のゼロポインタは実行時に逆参照されません。

    intのサイズで実行されたCのほとんどのシステムでは、32ビットでポインタと同じだったので、これは実際には機能しました。

  • -2

    offsetofマクロの実装は実際には無関係です。

    実際のC標準は7.17.3のようにそれを定義:

    size_tの型を持つ整数定数式に展開
    offsetof(type, member-designator) 
    

    、の値は構造体に、バイト単位でオフセットされています(メンバー指名者によって指定されている)を、その構造の始めから(タイプによって指定されている)。型式および指名子は、static type t;とするものとする。

    信頼アダムローゼンフィールドの答えです。

    Rは完全に間違っています。特に、プラットフォーム間でコードが移植できない場合には、多くの用途があります。 `offsetof`マクロが正しくないことを

    (OK、それはC++ですが、我々は我々のデータ構造は、プラットフォーム/バージョン間のサイズを変更しないことを確認するために、時間のアサーションをコンパイル静的テンプレートでそれを使用しています。)

    関連する問題