C/C++バイト揃え

6243 ワード

コンセプト
C言語では、構造は複合データ型であり、その構成要素は、int、long、floatなどの基本データ型の変数であってもよいし、配列、構造、結合などの複合データ型のデータユニットであってもよい.構造では、コンパイラは構造の各メンバーに空間を自然境界(alignment)で割り当てます.各メンバーは、宣言された順序でメモリに順次格納され、最初のメンバーのアドレスは構造全体のアドレスと同じです.
CPUが変数への迅速なアクセスを可能にするために、変数の開始アドレスは、いわゆる「整列」という特性を有しなければならない.例えば、4バイトのint型では、その開始アドレスは4バイトの境界に位置しなければならない.すなわち、開始アドレスは4で割り切れる、すなわち、整列はメモリ内の位置に関係する.変数のメモリアドレスがちょうどその長さの整数倍にある場合、彼は自然整列と呼ばれます.
例えば32ビットcpuでは、整数変数のアドレスが0 x 0000004(4の倍数)であると仮定すると、それは自然に整列し、アドレスが0 x 0000002(4の倍数ではない)である場合は非整列である.
現代のコンピュータではメモリ空間はbyteによって区分されており、理論的には任意のタイプの変数へのアクセスは任意のアドレスから開始できるようだが、実際には特定のタイプの変数にアクセスする際に特定のタイプのメモリアドレスにアクセスすることが多く、これは様々なタイプのデータが一定の規則に従って空間的に並ぶ必要がある.順番の次から次への排出ではなく、これが整列です.
なぜバイトを揃えるのか
バイト整列が必要な根本的な原因は,CPUがデータにアクセスする効率の問題にある.上記の整数型変数のアドレスが自然な位置合わせではないと仮定し、例えば0 x 0000002であれば、CPUはその値を取ると2回のメモリにアクセスし、1回目は0 x 0000002-0 x 000003の1つのshortから、2回目は0 x 0000004-0 x 00000 5の1つのshortから組み合わせて所望のデータを取得し、変数が0 x 0000003アドレスであれば3回のメモリにアクセスし、1回目はchar,2回目はshort,3回目はcharであり,その後組み合わせて整数データを得た.
変数が自然位置にある場合は、1回でデータを取り出すことができます.一部のシステムでは、sparcシステムのように、整列されていないデータを取得するとエラーが発生し、x 86ではエラーは発生しませんが、効率が低下します.
各ハードウェアプラットフォームは、ストレージスペースの処理に大きな違いがあります.一部のプラットフォームでは、特定のタイプのデータに対して、特定のアドレスからのみアクセスできます.
例えば、一部のアーキテクチャのCPUは、整列されていない変数にアクセスする際にエラーが発生する場合があります.このアーキテクチャの下でプログラミングするにはバイト整列を保証する必要がありますが、他のプラットフォームではそうではない可能性がありますが、最も一般的なのは、そのプラットフォームに適した要求に従ってデータ格納を整列させないと、アクセス効率に損失をもたらすことです.
例えば、一部のプラットフォームでは、毎回偶数アドレスから始まるプラットフォームがあり、int型(32ビットシステムと仮定)が偶数アドレスから始まる場所に格納されると、1リードサイクルで32 bitを読み出すことができ、奇数アドレスから始まる場所に格納されると、2リードサイクルが必要となり、2回読み出した結果の高低バイトをパッチワークして32 bitデータを得ることができる.明らかに読み取り効率が大幅に低下している.
また、バイトアライメントの役割は、cpuの迅速なアクセスを容易にするだけでなく、バイトアライメントを合理的に利用することで==ストレージスペースを効果的に節約することができます==
すなわち、CPUが1回アクセスする場合、0 x 01~0 x 04、0 x 05~0 x 08…ハードウェアは、1回のアクセスで0 x 02~0 x 05まで読むことをサポートしていない
例:0 x 02~0 x 05にintが1つ保存されている場合、このintを読み取るには、まず0 x 01~0 x 04を読み、0 x 02~0 x 04の内容を残し、さらに0 x 05~0 x 08を読み、0 x 05の内容を残し、2つの部分をつなぎ合わせてそのintの値を得る必要があり、このように1つのintを読むと2回のメモリアクセスが必要となり、効率が低下する.
配置
structまたはunionのstructまたはunionの場合、それらのバイト整列基準は、そのすべてのメンバーの中でバイト数が最も大きいデータのバイト数である.
一般的にC/C++の変数が占めるバイト数
char:1バイト;
short:2バイト;
int:4バイト;
long:4バイト;
long long:8バイト;
float:4バイト;
double:8バイト;
bool:1バイト;
*structでバイトの位置合わせに必要な条件:
1、ある変数が格納する開始位置の構造の開始位置に対するオフセット量は、その変数バイト数の整数倍である.
2、構造が占有する総バイト数は、構造種のバイト数が最も長い変数のバイト数の整数倍である.
例:
struct Struct
{
    double d1;
    char d2;
    int d3;
}a;

 sizeof(a) = 8 + 1 + 3 + 4 = 16.ここで補填された3バイトは、int型データの開始位置の構造開始位置に対するオフセット量を4の整数倍とするためである.
struct Struct
{
    char d1;
    double d2;
    int d3;
}b;

sizeof(b) = 1 + 7 + 8 + 4 = 20. 20/8=2・・・4なので、さらに4バイトを足して8の整数倍にする必要があります
*unionでバイトの位置合わせに必要な2つの条件:
1、unoinのサイズは最も広いメンバーを収容するのに十分でなければならない.
2、unionのサイズは、その含まれるベースメンバータイプのサイズで割り切れる必要があります.
 
バイトの位置合わせの別の方法
VCは、#pragma pack(n)を使用してバイト位置合わせをカスタマイズする
次の2つのケースがあります.
1、nが変数より大きいバイト数:オフセット量はデフォルトのバイト位置合わせのみを満たす;
2、nは変数が占めるバイト数より小さい:オフセット量はnの整数倍であり、デフォルトのバイト整列方式は使用されない.
例:
int main()
{
#pragma pack(push)        //       
#pragma pack(4)            //     4     
	struct test
	{
		char m1;
		double m2;
		int m3;
	}a;
#pragma pack(pop)        //       
	//sizeof(a) = 1 + 3 + 8 + 4 = 16  //           n    8,   m2                     n,   4.
	cout << sizeof(a)<

1、バイトアライメント問題の議論は以上で終わりました.次に、私が出会った2つの問題を例に挙げます.
#include 
union
{
    char x[5];
    int i;
}a;

int main()
{
    a.x[0] = 10;
    a.x[1] = 1;
    printf("%d
", a.i); printf("%d
", sizeof(a)); return 0; }

出力266
解析:
Union割り当てメモリにはバイト位置合わせの問題があり、上に詳しく説明されていますが、ここでは簡単に説明します.unionが割り当てるメモリは、union内のすべての基本データ型の倍数である必要があります.この問題では1と4の倍数であり、char x[5]が5バイトを占有するため、unionが割り当てたメモリサイズは8バイトであるべきである.Windowsシステムでは高バイトが後、低バイトが前です.一方char x[5]は、最初の2つの要素にのみ値があり、すなわち、2つの値は2バイトしか占めていない、すなわちunionのint型データには2ビット低い値しかない.すなわちiのバイナリ表現は、2^1+2^3+2^8=266も16 0 x 10 Aに相当する10*16^0+1*16^2=266である
ここでは、進数変換の問題を補足します.私も一緒に収集しました.
にしんそうかん
じっしん
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16進数
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
バイナリ
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
16進数変換10進数:
0 x 2 AF 5変換10進数:
      5 * 16^0 = 5            
      F * 16^1 = 240            
      A * 16^2 = 2560            
      2 * 16^3 = 8192            
      ---------------            
      5 + 240 + 2560 + 8192 = 10997    
10進数変換16進数:
2500変換16進数:
      2500/16 = 156 …… 4            
      156/16 = 9 …… 12(C)            
      9/16 = 0 …… 9            
      ----------------------            
4 C 9を得て、9 C 4を逆転して、つまり2500の16進数は:0 x 9 C 4を表します
2進数変換16進数:
    101110011011.1001        
四合一法、すなわち、バイナリの小数点を境界点とし、左(または右)に4桁ごとに1位をとる.        
成分がよくなったら、バイナリと16進数の対応表を照らし合わせて、4桁のバイナリを重みで加算して、得られた数は1桁の16進数で、それから順番に並べて、小数点の位置は変わらない.        
結果:B 9 B.9        
なお、左(または右)に4位をとる場合は、最上位(最下位)に4位を揃えることができなければ、小数点の一番左(または一番右)に0を足して換算することができます.    
16進数変換2進数:
バイナリと16進数の対応表を対照して、16進数をバイナリ数字に分け、4桁のバイナリ数字で重みを加えて、最後にバイナリ数字を得て、小数点は依然としてです.
2、
#include 
using namespace std;

typedef struct A
{
    char aChar;
    int aInt_2;
    short aInt;
}TypeA;

typedef struct B
{
    char bChar[3];
    TypeA bA;
    double bDouble;
}TypeB;

int main()
{
    TypeA a;
    TypeB b;
    cout << sizeof(a) << endl << sizeof(b) << endl;
    return 0;
}

出力12 24
解析:
上記のバイト整列問題の議論から,この問題の答えが容易に得られる.sizeof(TypeA) = 1 + 3 + 4 + 2 = 10. 10/4=2......2なので、sizeof(TypeA)=12という2バイトを追加する必要があります.sizeof(TypeB) = 3 + 1 + 12 + 8 = 24.1バイトを追加したのは、struct Type BにおけるType Aタイプのデフォルトの位置合わせが4バイト(つまりintのバイトサイズ、つまりstruct Type Aの中で最も長いバイト)であるためです.