【メモリアライメントの主文】C/C++バイトアライメントの詳細

5470 ワード

C/C++バイト整列の詳細
に発表
2010年09月05日
から
LinuxSong
一.バイト整列とは何ですか.なぜ整列しますか.
現代のコンピュータではメモリ空間はbyteによって区分されており、理論的には任意のタイプの変数へのアクセスは任意のアドレスから開始できるようだが、実際には特定のタイプの変数にアクセスする際に特定のタイプのメモリアドレスにアクセスすることが多く、これは様々なタイプのデータが一定の規則に従って空間的に並ぶ必要がある.順番の次から次への排出ではなく、これが整列です.
位置合わせの役割と原因:各ハードウェアプラットフォームはストレージ空間の処理に大きな違いがあります.一部のプラットフォームでは、特定のタイプのデータに対して、特定のアドレスからのみアクセスできます.例えば、一部のアーキテクチャのCPUは、整列を行う変数にアクセスする際にエラーが発生する場合、このようなアーキテクチャの下でプログラミングするにはバイト整列を保証しなければならない.他のプラットフォームではこのような状況はないかもしれませんが、最も一般的なのは、プラットフォームに適した要件に従ってデータの保存を整列しないと、アクセス効率に損失をもたらすことです.例えば、一部のプラットフォームでは、毎回偶数アドレスから始まるプラットフォームがあり、int型(32ビットシステムと仮定)が偶数アドレスから始まる場所に格納されると、1つのリードサイクルで32 bitを読み出すことができ、奇数アドレスから始まる場所に格納されると、2つのリードサイクルが必要となり、2回のリード結果の高低バイトをパッチワークして32 bitデータを得ることができる.明らかに読み取り効率が大幅に低下している.
二.バイト整列がプログラムに与える影響:
まずいくつかの例を見てみましょう(32 bit,x 86環境,gccコンパイラ):構造体を以下のように定義します.
struct A
{
  int a;
  char b;
  short c;
};

struct B
{
  char b;
  int a;
  short c;
};

32ビットマシン上の様々なデータ型の長さは、char:1(符号なし符号同)short:2(符号なし符号同)int:4(符号なし符号なし)long:4(符号なし符号同)float:4 double:8であることが知られていますが、上記の2つの構造の大きさはどうでしょうか.結果として、sizeof(strcut A)値が8 sizeof(struct B)の値は12
構造体Aには4バイト長のint 1個、1バイト長のchar 1個、2バイト長のshort型データ1個が含まれており、Bも同様である.A、Bの大きさは7バイトのはずです.上記の結果は、コンパイラがデータ・メンバーを空間的に整列させるためです.以上、コンパイラのデフォルト設定に従って位置合わせを行った結果、コンパイラのデフォルト設定を変更することができるかどうかはもちろん可能である.例:
#pragma pack (2) /*   2    */
struct C
{
  char b;
  int a;
  short c;
};
#pragma pack ()   /*      ,      */

sizeof(struct C)値は8です.位置合わせの値を1に変更します.
#pragma pack (1) /*   1    */
struct D
{
  char b;
  int a;
  short c;
};
#pragma pack () /*      ,      */

sizeof(struct D)値は7です.後で#pragma pack()の役割について説明します.
三.コンパイラはどのような原則で位置合わせされていますか?
まず、4つの重要な基本概念を見てみましょう.1.データ型自体の整列値:char型データの場合、自己整列値は1、short型の場合2、int、float、double型の場合、自己整列値は4、単位バイトです.2.構造体またはクラスの自己整列値:メンバーの自己整列値が最も大きい値.3.位置合わせ値の指定:#pragma pack(value)の場合の位置合わせ値valueの指定.4.データ・メンバー、構造体、クラスの有効な位置合わせ値:自己位置合わせ値と指定された位置合わせ値のうちの値.これらの値があれば、特定のデータ構造のメンバーとそれ自体の位置合わせを簡単に議論することができます.有効整列値Nは、最終的にデータ格納アドレスの決定に用いられる値であり、最も重要である.有効整列Nは、「Nに整列する」、すなわち、そのデータの「格納元アドレス%N=0」を表す.データ構造のデータ変数は、定義された順序で排出されます.最初のデータ変数の開始アドレスは、データ構造の開始アドレスです.構造体のメンバー変数は排出を整列させ、構造体自体も自身の有効整列値に基づいて丸めなければならない(すなわち、構造体のメンバー変数が総長を占めるには、構造体の有効整列値の整数倍が必要であり、以下の例に関連して理解される).これでは上記のいくつかの例の値が理解できません.例分析:分析例B:
struct B
{
  char b;
  int a;
  short c;
};

仮にBがアドレス空間0から×0000排出開始.この例では、指定した位置合わせ値は定義されていません.筆者の環境では、この値のデフォルトは4です.最初のメンバー変数bの自己整列値は1であり、指定またはデフォルト指定の整列値4よりも小さいため、有効整列値は1であるため、アドレス0が格納される×0000適合0×0000%1=0.2番目のメンバー変数aは、自身の位置合わせ値が4であるため、有効位置合わせ値も4であるため、開始アドレス0にしか格納できない×0004~0×0007この4つの連続するバイト空間のうち、0を確認する×0004%4=0で、最初の変数に近い.3番目の変数cは、自己整列値が2であるため、有効整列値も2であり、0に格納できる×0008~0×0009この2バイトの空間の中で、0に合っている×0008%2=0.だから0から×0000~0×0009保存されているのはすべてBコンテンツです.また、データ構造Bの自己整列値は、その変数の中で最大整列値(ここではb)であるので4であるため、構造体の有効整列値も4である.構造体の丸みの要求により、0×0009~0×0000=10バイト、(10+2)%4=0.したがって、0 x 0000 A~0 x 000 Bも構造体Bに占有される.だからBは0から×0000から0 x 000 Bまでの合計12バイト、sizeof(struct B)=12;実際には、この1つだけでバイトが整列している場合、その先頭アドレスは0であるため、整列しているに違いありません.後に2バイトを追加するのは、コンパイラが構造配列のアクセス効率を実現するために、構造Bの配列を定義した場合、最初の構造開始アドレスは0で問題ありませんが、2番目の構造は?配列の定義に従って、配列の中のすべての要素は隣接していて、もし私たちが構造の大きさを4の整数倍に補充しなければ、次の構造の開始アドレスは0 x 0000 Aで、これは明らかに構造のアドレスの位置合わせを満たすことができなくて、だから私たちは構造を有効な位置合わせの大きさの整数倍に補充します.例えば、char型データの自己整列値が1、short型が2、int、float、double型の自己整列値が4である、これらの既存型の自己整列値も配列に基づいて考慮するが、これらの型の長さが既知であるため、彼らの自己整列値も既知である.同様に、上記の例Cを分析する.
#pragma pack (2) /*   2    */
struct C
{
  char b;
  int a;
  short c;
};
#pragma pack () /*      ,      */

1番目の変数bの自己整列値は1であり、整列値が2であることを指定するので、その有効整列値は1であり、Cが0から×0000が開始すると、bは0に格納される×0000,適合0×0000%1= 0;2番目の変数は、自己整列値が4で、整列値が2であることを指定します.したがって、有効整列値は2であるため、順序は0に格納されます.×0002、0×0003、0×0004、0×0005 4バイト連続で0×0002%2=0.3番目の変数cの自己整列値は2であるため、有効整列値は2であり、順次0に格納される×0006、0×0007で0に一致×0006%2=0.だから0から×0000~0×00007の合計8バイトはCの変数である.また、Cの自己整列値は4であるため、Cの有効整列値は2である.また8%2=0、Cは0のみ×0000~0×0007の8バイト.だからsizeof(struct C)=8.
四.コンパイラのデフォルトの位置合わせ値を変更するにはどうすればいいですか?
1.VC IDEでは、[Project]|[Settings],c/c++タブCategoryのCode GenerationオプションのStruct Member Alignmentで変更できます.デフォルトは8バイトです.2.符号化時には、#pragma packのように動的に変更することができる.注意:progmaではなくpragmaです.
五.バイトの位置合わせについて、プログラミングでどう考えますか?
プログラミングの際にスペースの節約を考慮するならば、構造のヘッダアドレスが0であると仮定し、各変数を上記の原則に従って並べるだけでよい.基本的な原則は、構造中の変数をタイプの大きさで小さいから大きいまで宣言し、できるだけ中間の埋め込み空間を減らすことである.もう1つは、時間を空間的に置き換える効率のために、reservedメンバーを明示的に挿入するために、空間を埋めて位置合わせすることを示します.例えば、空間的に時間を置き換える方法があります.
struct A{
  char a;
  char reserved[3];//       
  int b;
};

reservedメンバーは私たちのプログラムにとって何の意味もありません.それはバイトの位置合わせの目的を達成するために空間を埋めるだけです.もちろん、このメンバーを追加しなくても通常コンパイラは私たちに自動的に位置合わせを埋めてくれます.私たち自身がそれを加えるのは明示的な注意の役割を果たすだけです.
六.バイトの位置合わせがもたらす危険性:
コードの位置合わせに関する危険性は、暗黙的なものが多い.例えば、強制型変換の場合.例:unsigned int i=0×12345678; unsigned char *p=NULL; unsigned short *p1=NULL;
p=&i; *p=0×00; p1=(unsigned short *)(p+1); *p1=0×0000; 最後の2つのコードは、奇数境界からunsignedshort型変数にアクセスし、明らかに整列の規定に合致しない.x 86では、同様の動作は効率にのみ影響するが、MIPSまたはsparcでは、バイト整列が必要であるためerrorである可能性がある.七.バイトの位置合わせに関する質問を検索する方法:
位置合わせまたは値付けの問題が発生した場合は、まず1を表示します.コンパイラのbig little端子設定2.このシステム自体が非整列アクセスをサポートしているかどうかを見る.整列が設定されているかどうかをサポートしている場合は、アクセスがない場合は、特別なアクセス操作を示すために特定の修飾を追加する必要があります.
このエントリはC/C++分類ディレクトリに発表され、c/c++ラベルが貼られています.お気に入りに固定リンクを追加します.
←Linuxシャットダウンと再起動コマンドの詳細
linuxにおけるフルスクリーン方式連続実行コマンド

『C/C++バイト整列の詳細』には1つのコメントがあります
Pingbackリファレンス通知:GNU C–8086のメモリアクセスメカニズムおよびメモリアライメントについて|プログラミング・モーニング