1. 構造体はメモリにどのように保存されますか?int メイン() { 学生スタ; スタッ.id = 123456; strcpy(stu.name,"feizhufeifei"); stu.math = 90; ステュー.PE = 80; printf("生徒:%p\r\n",&stu); printf("stu.ID:%p\r\n",&stu.ID); printf("stu.name:%p\r\n",&stu.name); printf("stu.math:%p\r\n",&stu.math); 0を返します。 } 印刷結果は次のとおりです。 //構造体のアドレス Student:0xffffcbb0 //構造体の最初のメンバーのアドレス stu.ID:0xffffcbb0 //オフセットアドレス + 0 stu.name:0xffffcbb4//オフセットアドレス + 4 stu.math:0xffffcbd4 //オフセットアドレス + 24 構造体のアドレスは構造体の最初のメンバーのアドレスと同じであることがわかります。これが、「車輪の再発明を拒否! Linux カーネルの一般的なリンク リストを移植して使用する方法 (完全なコード実装付き)」で、構造体の最初に struct list_head を配置する必要がある理由について説明した理由です。 よくわからない場合は、次の 2 つの例を見てください。
構造体のメンバー変数は実際にはメモリ内のオフセット アドレスとして格納されていることがわかります。つまり、構造体 A のアドレス + メンバー変数のオフセット アドレス = 構造体メンバー変数の開始アドレスです。 したがって、構造体変数の開始アドレスとメンバー変数のオフセット アドレスに基づいて、構造体 A のアドレスを推測することもできます。 2. container_ofマクロ#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER) #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member)*__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); }) まず、3 つのパラメータを見てみましょう。ptr はメンバー変数へのポインタ、type は構造体の型、member はメンバー変数の名前です。 container_of マクロの機能は、構造体内のメンバー変数のアドレス、変数名、および構造体の型を通じて構造体変数のアドレスを見つけることです。ここで使用されるトリックは、コンパイラ テクノロジを使用することです。これは、まず構造体内の構造体メンバーのオフセットを見つけ、次にメンバー変数のアドレスに基づいてメイン構造体変数のアドレスを見つけるというものです。以下、各部分の詳細な分析です。 3. 型まず、変数の型を返すために使用される typeof を見てみましょう。これは GCC コンパイラの拡張機能であり、typeof はコンパイラに依存します。これは C 言語仕様では必須ではなく、標準の一部でもありません。 int メイン() { 整数a = 5; //ここでaと同じ型の変数bを定義します 型aはb = 6です。 printf("%d,%d\r\n",a,b);//5 6 0を返します。 } 4. (((タイプ *)0)->メンバー)((TYPE *)0) は、0 を type 型の構造体ポインタに変換します。言い換えると、コンパイラは構造体がプログラム セグメントの先頭 0 から始まると考えます。アドレス 0 から始まる場合、取得するメンバー変数のアドレスは、メンバー変数のオフセット アドレスと直接等しくなります。 (((type *)0)->member) は、構造体内の MEMBER メンバーを参照します。 typedef 構造体学生{ 整数ID; 文字名[30]; 整数数学; }学生; int メイン() { //ここでは、構造体が強制的にアドレス 0 に変換され、その後、name のアドレスが出力されます。 printf("%d\r\n",&((生徒 *)0)->名前);//4 0を返します。 } 5. const typeof(((type * )0) ->member)*__mptr = (ptr);このコードは、 なぜこれを行う代わりに 6. offsetof(型、メンバー)
size_t は標準 C ライブラリで定義されており、通常 32 ビット アーキテクチャでは次のように定義されます。
64 ビット アーキテクチャでは次のように定義されます。
定義からわかるように、size_t は負でない数なので、通常はカウントに size_t が使用されます (カウントには負の領域は必要ないため)。
プログラムの移植性を高めるために、カーネルは int と unsigned の代わりに 7. (type * )((char * )__mptr - offsetof(type, member))この文は、 8. 例#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) typedef 構造体 学生 { 整数ID; 文字名[30]; 整数数学; }学生; int メイン() { 学生スタ; 学生 *sptr = NULL; スタッ.id = 123456; strcpy(stu.name,"zhongyi"); stu.math = 90; sptr = container_of(&stu.id,学生、id); printf("sptr=%p\n",sptr); sptr = container_of(&stu.name,生徒,名前); printf("sptr=%p\n",sptr); sptr = container_of(&stu.math,学生,id); printf("sptr=%p\n",sptr); 0を返します。 } 結果は次のとおりです。
マクロ展開でより明確になるかもしれない int メイン() { 学生スタ; 学生 *sptr = NULL; スタッ.id = 123456; strcpy(stu.name,"zhongyi"); stu.math = 90; //展開して置換 sptr = ({ const unsigned char *__mptr = (&stu.id); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->id) );}); printf("sptr=%p\n",sptr); //展開して置換 sptr = ({ const unsigned char *__mptr = (&stu.name); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->name) );}); printf("sptr=%p\n",sptr); //展開して置換 sptr = ({ const unsigned int *__mptr = (&stu.math); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->math) );}); printf("sptr=%p\n",sptr); 0を返します。 } LinuxカーネルのContainer_Ofマクロの詳細説明はこれで終了です。LinuxカーネルのContainer_Ofマクロの詳細については、123WORDPRESS.COMの過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも123WORDPRESS.COMをよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: 実用的な MySQL + PostgreSQL バッチ挿入更新 insertOrUpdate
>>: JSコンストラクタとインスタンス化およびプロトタイプ導入の関係
この記事では、弾性ナビゲーション効果を実現するためのJavaScriptの具体的なコードを参考までに...
まず、インターネット上の一般的な慣行を見てみましょうデフォルトでは、プライベート ライブラリはイメー...
のようにLIKE ではデータ全体が一致する必要がありますが、REGEXP では部分的な一致のみが必要...
さっそく、コードを直接投稿します。具体的なコードは次のとおりです。 <!--方法 1: onf...
WML (ワイヤレス マークアップ言語)。これは HTML から派生したマークアップ言語ですが、W...
ElementUIはテーブルリストのページング効果のチュートリアルを実装しています。参考までに。具体...
Unix/Linux サービスsystemd サービス操作プロセス1. JDKがインストールされたC...
ブラウザで入力カーソルがずれる問題の詳しい説明<br />仕事で問題に遭遇し、解決策を探...
この記事では、MySQL 8.0.16圧縮パッケージのインストールと設定方法を参考までに紹介します。...
要約すると: Readonly は入力 (テキスト/パスワード) とテキスト領域に対してのみ有効です...
デフォルトでは、Linux の MySQL はテーブル名の大文字と小文字を区別します。 MySQL ...
成果を達成する実装コードhtml <input type="radio" ...
MySQLサービスを停止するWindowsでは、マイコンピュータを右クリック--管理--サービスと...
序文ご存知のとおり、nginx 構成ファイルは add_header ディレクティブを使用して応答ヘ...
この記事では、MySQL 8.0.16 winx64.zipのインストールと設定方法の具体的なコード...