2011-01-06 11 views
5

次のプログラムがクラッシュしなかったとき、私は驚いた。ポインタを使って構造体要素にアクセスする

typedef struct _x { 
    int a; 
    char b; 
    int c; 
} x; 

main() { 
    x *ptr = 0; 
    char *d = &ptr->b; 
} 

は、私の理解あたりとして->オペレータは&演算子より優先順位が高いです。だから私はNULLポインタを逆参照しようとすると、以下のステートメントでプログラムがクラッシュすると考えましたtr

char *d = &ptr->b; 

しかし、文&ptr->bは有効なアドレスを評価します。誰かが私が間違っている場所を説明してもらえますか?アドレス*((x*)0)に対して(タイプintである)_x.a_xbのオフセットを意味

+0

これは 'offsetof'マクロと何らかの形で似ています。 – ruslik

答えて

2

&ptr->b == sizeof(int)、。 4(32ビットアーキテクチャでは一般的)のオフセットはdポインタ内に保存されます。 segフォルトを取得するには、dにアクセスする必要があります。

4

コードがクラッシュしないのは、実際にポインタを間接参照していないということです。表現

&ptr->b 

が実際ptrまたはptr->bの内容をロードしようとしていないことに注意してください。代わりに、これはメモリ内のどこにあるかのアドレスを格納するだけです。あなたが得ることになるのは、オブジェクトのbフィールドが指し示す場所のポインタがptrであるはずです。これはアドレス0より数バイト先になるので、作成したばかりのポインタをデリファレンスするとsegfaultが発生します。

2

アドレスを計算するためにメモリにアクセスする必要はありません。 &ptr->bは、「ptrが指す構造のbフィールドのアドレスを私に教えてください」という意味です。そうすることで、そのメモリ位置に格納されているものを見る必要はありません。

構造体の代わりに配列のインデックスを作成することを検討すると役に立ちます。 Cはptr[5]*(ptr + 5)と等価と定義しています。つまり、&(ptr[5])&(*(ptr + 5))と同じです。今すぐ&*が「キャンセル」して(ptr + 5)になります。これにはポインタのインクリメントのみが含まれ、メモリからのロードはありません。

Cは、左辺値と右辺値を区別しているため、これはやや曇っています。つまり、メモリを参照する式は、式の左側では右側よりも扱いが異なります。 x = y;のような文が与えられると、Cコンパイラはアドレスyから値をロードし、アドレスxに格納します。これは区別です:yは暗黙的に参照解除されますが、xは参照されません。

5

あなたの期待は根拠がありませんでした。 nullポインタを間接参照すると、Cプログラムは必ずしも "クラッシュ"するとは限りません。このようなことをしようとすると、Cプログラムはという未定義の動作と呼ばれます。未定義の振る舞いは、さまざまな形で現れます。クラッシュする可能性があります。あるいは、「働く」プログラムに似ているものを作り出すこともできます。後者は、明らかにあなたの場合に起こったものです。

いずれにしても、プログラムの動作は未定義です。そして、あなたが誤って信じているように、「有効なアドレス」を生成しません。オブジェクトが存在しないメモリ内の位置に対応する数値アドレスは有効ではありません(nullポインタ値は例外です)。

関連する問題