1 -- 結(jié)構(gòu)體數(shù)據(jù)成員對齊的意義 許多實(shí)際的計算機(jī)系統(tǒng)對基本類型數(shù)據(jù)在內(nèi)存中存放的位置有限制,它們會要求這些數(shù)據(jù)的起始地址的值是某個數(shù)k的倍數(shù),這就是所謂的內(nèi)存對齊,而這個k則被稱為該數(shù)據(jù)類型的對齊模數(shù)(alignment modulus)。這種強(qiáng)制的要求一來簡化了處理器與內(nèi)存之間傳輸系統(tǒng)的設(shè)計,二來可以提升讀取數(shù)據(jù)的速度。 比如這么一種處理器,它每次讀寫內(nèi)存的時候都從某個8倍數(shù)的地址開始,一次讀出或?qū)懭?個字節(jié)的數(shù)據(jù),假如軟件能保證double類型的數(shù)據(jù)都從8倍數(shù)地址開始,那么讀或?qū)懸粋€double類型數(shù)據(jù)就只需要一次內(nèi)存操作。否則,我們就可能需要兩次內(nèi)存操作才能完成這個動作,因?yàn)閿?shù)據(jù)或許恰好橫跨在兩個符合對齊要求的8字節(jié)內(nèi)存塊上。 2 -- 結(jié)構(gòu)體對齊包括兩個方面的含義 1)結(jié)構(gòu)體總長度; 2)結(jié)構(gòu)體內(nèi)各數(shù)據(jù)成員的內(nèi)存對齊,即該數(shù)據(jù)成員相對結(jié)構(gòu)體的起始位置; 3 -- 結(jié)構(gòu)體大小的計算方法和步驟 1)將結(jié)構(gòu)體內(nèi)所有數(shù)據(jù)成員的長度值相加,記為sum_a; 2)將各數(shù)據(jù)成員為了內(nèi)存對齊,按各自對齊模數(shù)而填充的字節(jié)數(shù)累加到和sum_a上,記為sum_b。對齊模數(shù)是#pragma pack指定的數(shù)值以及該數(shù)據(jù)成員自身長度中數(shù)值較小者。該數(shù)據(jù)相對起始位置應(yīng)該是對齊模式的整數(shù)倍; 3)將和sum_b向結(jié)構(gòu)體模數(shù)對齊,該模數(shù)是【#pragma pack指定的數(shù)值】、【未指定#pragma pack時,系統(tǒng)默認(rèn)的對齊模數(shù)(32位系統(tǒng)為4字節(jié),64位為8字節(jié))】和【結(jié)構(gòu)體內(nèi)部最大的基本數(shù)據(jù)類型成員】長度中數(shù)值較小者。結(jié)構(gòu)體的長度應(yīng)該是該模數(shù)的整數(shù)倍。 4 -- 結(jié)構(gòu)體大小計算舉例 在計算之前,我們首先需要明確的是各個數(shù)據(jù)成員的對齊模數(shù),對齊模數(shù)和數(shù)據(jù)成員本身的長度以及pragma pack編譯參數(shù)有關(guān),其值是二者中最小數(shù)。如果程序沒有明確指出,就需要知道編譯器默認(rèn)的對齊模數(shù)值。下表是Windows XP/DEV-C++和Linux/GCC中基本數(shù)據(jù)類型的長度和默認(rèn)對齊模數(shù)。
例子1: struct my_struct { char a; long double b; }; 此例子Windows和Linux計算方法有些許不一致。 #pragma pack(2) struct my_struct { char a; long double b; }; #pragma pack() 例子1和例子2不同之處在于例子2中使用了#pragma pack(2)編譯參數(shù),它強(qiáng)制指定對齊模數(shù)是2。此例子Windows和Linux計算方法有些許不一致。 struct my_struct { char a; double b; char c; }; 前兩例中,數(shù)據(jù)成員在Linux和Windows下都相同,例3中double的對齊模數(shù)在Linux中是4,在Windows下是8,針對這種模數(shù)不相同的情況加以分析。 struct my_struct { char a[11]; int b; char c; }; 此例子Windows和Linux計算方法一樣,如下: struct my_test { int my_test_a; char my_test_b; }; struct my_struct { struct my_test a; double my_struct_a; int my_struct_b; char my_struct_c; }; 例子5和前幾個例子均不同,在此例子中我們要計算struct my_struct的大小,而my_struct中嵌套了一個my_test結(jié)構(gòu)體。這種結(jié)構(gòu)體應(yīng)該如何計算呢?原則是將my_test在my_struct中先展開,然后再計算,即是展開成如下結(jié)構(gòu)體: struct my_struct { int my_test_a; char my_test_b; double my_struct_a; int my_struct_b; char my_struct_c; }; 此例子Windows中的計算方法如下: 5 -- 源代碼附錄
上面的例子均在Windows(VC++6.0)和Linux(GCC4.1.0)上測試驗(yàn)證。下面是測試程序。 #include <iostream> #include <stdio.h> using namespace std; int main() { cout << "sizeof(char) = " << sizeof(char) << endl; cout << "sizeof(short) = " << sizeof(short) << endl; cout << "sizeof(int) = " << sizeof(int) << endl; cout << "sizeof(long) = " << sizeof(long) << endl; cout << "sizeof(float) = " << sizeof(float) << endl; cout << "sizeof(double) = " << sizeof(double) << endl; cout << "sizeof(long long) = " << sizeof(long long) << endl; cout << "sizeof(long double) = " << sizeof(long double) << endl << endl; // 例子1 { struct my_struct { char a; long double b; }; cout << "exapmle-1: sizeof(my_struct) = " << sizeof(my_struct) << endl; struct my_struct data; printf("my_struct->a: %u\nmy_struct->b: %u\n\n", &data.a, &data.b); } // 例子2 { #pragma pack(2) struct my_struct { char a; long double b; }; #pragma pack() struct my_struct data; cout << "exapmle-2: sizeof(my_struct) = " << sizeof(my_struct) << endl; printf("my_struct->a: %u\nmy_struct->b: %u\n\n", &data.a, &data.b); } // 例子3 { struct my_struct { char a; double b; char c; }; struct my_struct data; cout << "exapmle-3: sizeof(my_struct) = " << sizeof(my_struct) << endl; printf("my_struct->a: %u\nmy_struct->b: %u\nmy_struct->c: %u\n\n", &data.a, &data.b, &data.c); } // 例子4 { struct my_struct { char a[11]; int b; char c; }; cout << "example-4: sizeof(my_struct) = " << sizeof(struct my_struct) << endl; struct my_struct data; printf("my_struct->a: %u\nmy_struct->b: %u\nmy_struct->c: %u\n\n", &data, &data.b, &data.c); } // 例子5 { struct my_test { int my_test_a; char my_test_b; }; struct my_struct { struct my_test a; double my_struct_a; int my_struct_b; char my_struct_c; }; cout << "example-5: sizeof(my_struct) = " << sizeof(struct my_struct) << endl; struct my_struct data; printf("my_struct->my_test_a : %u\n" "my_struct->my_test_b : %u\n" "my_struct->my_struct_a: %u\n" "my_struct->my_struct_b: %u\n" "my_struct->my_struct_c: %u\n", &data.a.my_test_a, &data.a.my_test_b, &data.my_struct_a, &data.my_struct_b, &data.my_struct_c); } return 0; }執(zhí)行結(jié)果: //Linux localhost 3.4.6-2.10-desktop #1 SMP PREEMPT Thu Jul 28 19:20:26 UTC 2012 (641c197) x86_64 x86_64 x86_64 GNU/Linux sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 8 sizeof(float) = 4 sizeof(double) = 8 sizeof(long long) = 8 sizeof(long double) = 16 exapmle-1: sizeof(my_struct) = 32 my_struct->a: 2163695552 my_struct->b: 2163695568 exapmle-2: sizeof(my_struct) = 18 my_struct->a: 2163695680 my_struct->b: 2163695682 exapmle-3: sizeof(my_struct) = 24 my_struct->a: 2163695648 my_struct->b: 2163695656 my_struct->c: 2163695664 example-4: sizeof(my_struct) = 20 my_struct->a: 2163695616 my_struct->b: 2163695628 my_struct->c: 2163695632 example-5: sizeof(my_struct) = 24 my_struct->my_test_a : 2163695584 my_struct->my_test_b : 2163695588 my_struct->my_struct_a: 2163695592 my_struct->my_struct_b: 2163695600 my_struct->my_struct_c: 2163695604 //Linux localhost 3.4.6-2.10-desktop #1 SMP PREEMPT Thu Jul 26 09:36:26 UTC 2012 (641c197) i686 i686 i386 GNU/Linux sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 4 sizeof(float) = 4 sizeof(double) = 8 sizeof(long long) = 8 sizeof(long double) = 12 exapmle-1: sizeof(my_struct) = 16 my_struct->a: 3213889904 my_struct->b: 3213889908 exapmle-2: sizeof(my_struct) = 14 my_struct->a: 3213889890 my_struct->b: 3213889892 exapmle-3: sizeof(my_struct) = 16 my_struct->a: 3213889872 my_struct->b: 3213889876 my_struct->c: 3213889884 example-4: sizeof(my_struct) = 20 my_struct->a: 3213889852 my_struct->b: 3213889864 my_struct->c: 3213889868 example-5: sizeof(my_struct) = 24 my_struct->my_test_a : 3213889828 my_struct->my_test_b : 3213889832 my_struct->my_struct_a: 3213889836 my_struct->my_struct_b: 3213889844 my_struct->my_struct_c: 3213889848 //CYGWIN_NT-6.1 motadou-PC 1.7.20(0.266/5/3) 2013-06-07 11:11 i686 Cygwin sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 4 sizeof(float) = 4 sizeof(double) = 8 sizeof(long long) = 8 sizeof(long double) = 12 exapmle-1: sizeof(my_struct) = 16 my_struct->a: 2272336 my_struct->b: 2272340 exapmle-2: sizeof(my_struct) = 14 my_struct->a: 2272322 my_struct->b: 2272324 exapmle-3: sizeof(my_struct) = 24 my_struct->a: 2272296 my_struct->b: 2272304 my_struct->c: 2272312 example-4: sizeof(my_struct) = 20 my_struct->a: 2272276 my_struct->b: 2272288 my_struct->c: 2272292 example-5: sizeof(my_struct) = 24 my_struct->my_test_a : 2272248 my_struct->my_test_b : 2272252 my_struct->my_struct_a: 2272256 my_struct->my_struct_b: 2272264 my_struct->my_struct_c: 2272268 |
|