6

私はこのコードを持っています。ここではnullポインターを逆参照しているように見えますが、結果をビット単位でAND演算しunsigned intます。私は本当に全体を理解していません。それは何をするつもりですか?これはポインタ演算の形式ですか?

struct hi  
{
   long a;  
   int b;  
   long c;  
};  

int main()  
{  
    struct hi ob={3,4,5};  
    struct hi *ptr=&ob;  
    int num= (unsigned int) & (((struct hi *)0)->b);  

   printf("%d",num);  
   printf("%d",*(int *)((char *)ptr + (unsigned int) & (((struct hi *)0)->b)));  
}  

私が得る出力は44です。しかし、それはどのように機能しますか?

4

5 に答える 5

8

nullポインタを実際に逆参照しているわけではありません。コード全体を確認する必要があります。コードの内容は次のとおりです。数値を取得0し、それをとして扱い、それが指す構造体のstruct hi *要素を選択し、この要素のアドレスを取得します。bこの操作の結果はb、構造体の先頭からの要素のオフセットになります。それをポインタに追加すると、にb等しい要素が得られ4ます。

于 2010-12-26T04:53:24.853 に答える
7

これにより、構造b体内のフィールドのオフセットがバイト単位で提供されますhi

((struct hi *)0)hiアドレスで始まる構造体へのポインタ0です。

(((struct hi *)0)->b)b上記の構造体のフィールドです

& (((struct hi *)0)->b)上記のフィールドのアドレスです。hi構造体はアドレスにあるため0、これは構造体内のオフセットですb

(unsigned int) & (((struct hi *)0)->b)は、アドレスタイプから、への変換でありunsigned int、数値として使用できます。

NULL実際にポインタを逆参照しているわけではありません。ポインタ演算を行っているだけです。


(((struct hi *)0)->b)禁止されているメモリ位置にアクセスしようとしているため、アクセスするとセグメンテーション違反が発生します。

使用すると、その禁止されたメモリ位置のアドレス& (((struct hi *)0)->b)のみを取得しているため、セグメンテーション違反は発生しませんが、その位置にアクセスしようとはしていません。

于 2010-12-26T04:52:45.970 に答える
3

32ビットコンパイル(またはWindowsでは64ビットコンパイル)を使用している必要があります。

最初の式--for--は、 ;からのマクロnumの一般的な実装です。ポータブルではありませんが、多くの場合機能します。offsetof<stddef.h>

2番目の式はそれを0(nullポインター)に追加し、同じ答え ptr-4を返します。2番目の式は、を指す オブジェクトのベースアドレスに4を追加します。これは、構造体の値4です。

出力に改行が含まれていません-おそらく必要です(改行を含めない場合は実装で定義されているため、動作は完全に移植可能ではありません:C99§7.19.2:"最後の行に改行文字の終了が必要かどうか実装定義です。」)。Unixボックスでは、44の直後に次のプロンプトが表示されるため、面倒です。

于 2010-12-26T04:54:06.853 に答える
3

これは「and」ではありません。これは右側の引数のアドレスを取得しています。
これは、実行時に構造体メンバーのオフセットを取得するための標準的なハックです。struct hiへのポインタに0をキャストしてから、「b」メンバーを参照してそのアドレスを取得しています。次に、このオフセットをポインタ「ptr」に追加し、ptrが指す構造体の「b」フィールドの実際のアドレスを取得します。これはobです。次に、そのポインターをintポインターにキャストして戻し(bはintであるため)、出力します。これは2枚目のプリントです。最初の出力はnumを出力します。これは、bの値が4であるためではなく、4がhistructのbフィールドのオフセットであるためです。これはsizeof(int)です。これは、bがaの後に続き、aがintであるためです...これが理にかなっていることを願っています:)

于 2010-12-26T04:56:47.417 に答える
0

NULLポインターの逆参照と、逆参照と見なされない場合の違いを理解する必要があることを明確にするためです。仕様は、実際には逆参照が発生しないことを示しており、式に&(address-of)演算子を含めると、実際には最適化されます。

したがって、&((struct T *)0)-> b)は、実際には->を最適化し、オフセット0からそのバイト数をジャンプして、それがstructT*であると想定します。これは、初心者にとっては本当にわかりにくいものです。ただし、これはLinuxカーネルで広く使用されており、list_entry、list_head、および初心者が理解できないさまざまなポインター演算の魔法の実際の感覚を提供します。

いずれにせよ、これは構造体Tオブジェクト内の「b」のオフセットを見つけるプログラム的な方法です。これは、offsetofだけでなく、list_entryなどの他のlist_head操作でも使用されます。

詳細については、RobertLoveの著書「LinuxKernelDevelopment」でこれについて読むことができます。

于 2011-01-01T21:51:46.303 に答える