c语言对齐方式研究笔记
c語言對(duì)齊方式研究筆記
為什么要對(duì)齊
TragicJun 發(fā)表于 2006-9-18 9:41:00 現(xiàn)代計(jì)算機(jī)中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對(duì)任何類型的變量的訪問可以從任何地址開始,但實(shí)際情況是在訪問特定類型變量的時(shí)候經(jīng)常在特定的內(nèi)存地址訪問,這就需要各種類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序的一個(gè)接一個(gè)的排放,這就是對(duì)齊。
對(duì)齊的作用和原因:各個(gè)硬件平臺(tái)對(duì)存儲(chǔ)空間的處理上有很大的不同。一些平臺(tái)對(duì)某些特定類型的數(shù)據(jù)只能從某些特定地址開始存取。比如有些架構(gòu)的CPU在訪問一個(gè)沒有進(jìn)行對(duì)齊的變量的時(shí)候會(huì)發(fā)生錯(cuò)誤,那么在這種架構(gòu)下編程必須保證字節(jié)對(duì)齊.其他平臺(tái)可能沒有這種情況,但是最常見的是如果不按照適合其平臺(tái)要求對(duì)數(shù)據(jù)存放進(jìn)行對(duì)齊,會(huì)在存取效率上帶來損失。所謂內(nèi)存對(duì)齊,是為了讓內(nèi)存存取更有效率而采用的一種編譯階段優(yōu)化內(nèi)存存取的手段。
比如對(duì)于int x;(這里假設(shè)sizeof(int)==4),因?yàn)閏pu對(duì)內(nèi)存的讀取操作是對(duì)齊的,如果x的地址不是4的倍數(shù),那么讀取這個(gè)x,需要讀取兩次共8個(gè)字節(jié),然后還要將其拼接成一個(gè)int,這比存取對(duì)齊過的x要麻煩很多。(比如操作系統(tǒng)一次允許讀4個(gè)字節(jié),從內(nèi)存4-7存放的是一個(gè)int,此時(shí)如果從地址4開始讀,恰好可以讀出一個(gè)int,但是如果從地址3開始讀,需要讀兩次,然后進(jìn)行拼接,形成一個(gè)int)。
怎么對(duì)齊
結(jié)構(gòu)體對(duì)齊可以分為兩種情況,結(jié)構(gòu)體對(duì)齊和結(jié)構(gòu)體內(nèi)成員對(duì)齊
- 結(jié)構(gòu)體對(duì)齊
結(jié)構(gòu)體對(duì)齊使用 **__attribute__((aligned(n)))**偽指令來設(shè)置結(jié)構(gòu)體n字節(jié)對(duì)齊,在嵌入式中FATFS一般會(huì)要求緩沖區(qū)512字節(jié)對(duì)齊。
有以下結(jié)構(gòu)體
typedef struct{char a;int b;} pack4_t __attribute__((aligned(512)));使用 __attribute__((aligned(512)))偽指令來設(shè)置結(jié)構(gòu)體512字節(jié)對(duì)齊,則a的地址一定是512的倍數(shù)。
- 結(jié)構(gòu)體內(nèi)成員對(duì)齊
結(jié)構(gòu)體內(nèi)成員對(duì)齊使用 , 一般聯(lián)合體或者結(jié)構(gòu)體強(qiáng)制轉(zhuǎn)化時(shí),需要注意結(jié)構(gòu)體成員對(duì)齊信息
#pragma pack(n) //n字節(jié)對(duì)齊
/* 用戶代碼 */
…
#pragma pack() //恢復(fù)默認(rèn)對(duì)齊
偽指令來設(shè)置結(jié)構(gòu)體內(nèi)成員n字節(jié)對(duì)齊
有以下結(jié)構(gòu)體
typedef struct{char a;int b;} pack4_t;在MDK默認(rèn)情況下,分配內(nèi)存時(shí),a時(shí)char類型,占一個(gè)字節(jié),所以對(duì)對(duì)齊沒有要求在a分配內(nèi)存后,會(huì)接著a的內(nèi)存地址對(duì)b進(jìn)行內(nèi)存分配。
由于b是int類型,占4個(gè)字節(jié),所以默認(rèn)會(huì)4字節(jié)對(duì)齊,也就是b的地址應(yīng)該是4的倍數(shù),如果a挨著的地址不是4的倍數(shù),則會(huì)向后順延,直至地址為4的倍數(shù),然后將對(duì)應(yīng)的地址空間分配給b,a和b中間的地址空間則為空的,其他的類似。
使用1字節(jié)對(duì)齊時(shí),a時(shí)char類型,占一個(gè)字節(jié),所以對(duì)對(duì)齊沒有要求在a分配內(nèi)存后,會(huì)接著a的內(nèi)存地址對(duì)b進(jìn)行內(nèi)存分配。
由于b是int類型,占4個(gè)字節(jié),默認(rèn)會(huì)4字節(jié)對(duì)齊,但是用戶指定了使用1字節(jié)對(duì)齊,所以b也使用1字節(jié)對(duì)齊,也就是b的地址應(yīng)該緊挨著a,其他的類似。
使用1字節(jié)對(duì)齊時(shí),a時(shí)char類型,占一個(gè)字節(jié),所以對(duì)對(duì)齊沒有要求在a分配內(nèi)存后,會(huì)接著a的內(nèi)存地址對(duì)b進(jìn)行內(nèi)存分配。
由于b是int類型,占4個(gè)字節(jié),默認(rèn)會(huì)4字節(jié)對(duì)齊,但是用戶指定了使用2字節(jié)對(duì)齊,所以b使用2字節(jié)對(duì)齊,也就是b的地址應(yīng)該是2的倍數(shù),如果a挨著的地址不是2的倍數(shù),則會(huì)向后順延,直至地址為2的倍數(shù),然后將對(duì)應(yīng)的地址空間分配給b,a和b中間的地址空間則為空的,其他的類似。
使用1字節(jié)對(duì)齊時(shí),a時(shí)char類型,占一個(gè)字節(jié),所以對(duì)對(duì)齊沒有要求在a分配內(nèi)存后,會(huì)接著a的內(nèi)存地址對(duì)b進(jìn)行內(nèi)存分配。
由于b是int類型,占4個(gè)字節(jié),所以默認(rèn)會(huì)4字節(jié)對(duì)齊,也就是b的地址應(yīng)該是4的倍數(shù),如果a挨著的地址不是4的倍數(shù),則會(huì)向后順延,直至地址為4的倍數(shù),然后將對(duì)應(yīng)的地址空間分配給b,a和b中間的地址空間則為空的,這里看起來和MDK默認(rèn)的分配一樣,但是當(dāng)b為double類型,占8個(gè)字節(jié)時(shí),當(dāng)使用默認(rèn)分配時(shí),b的地址需要是8的數(shù)倍,而這里只需要是4的倍數(shù)。
為了更加清楚理解,這里讓結(jié)構(gòu)體更復(fù)雜點(diǎn)
typedef struct{char a;int b;char c;uint16_t d;int e;char f;double g;} pack4_t;在STM32上實(shí)測(cè)
#pragma pack(4) typedef struct{char a;int b;char c;uint16_t d;int e;char f;double g;} pack4_t __attribute__((aligned(512))); #pragma pack()#pragma pack(2) typedef struct{char a;int b;char c;uint16_t d;int e;char f;double g;} pack2_t __attribute__((aligned(512))); #pragma pack()#pragma pack(1) typedef struct{char a;int b;char c;uint16_t d;int e;char f;double g;} pack1_t __attribute__((aligned(512))); #pragma pack()typedef struct{char a;int b;char c;uint16_t d;int e;char f;double g;} pack_t __attribute__((aligned(512)));pack4_t pack4 ; pack2_t pack2 ; pack1_t pack1 ; pack_t pack ;int main(void) { printf("\r\n1字節(jié)對(duì)齊 %x %x %x %x %x %x %x\r\n",&pack1.a, &pack1.b, &pack1.c, &pack1.d, &pack1.e , &pack1.f, &pack1.g);printf("\r\n2字節(jié)對(duì)齊 %x %x %x %x %x %x %x\r\n",&pack2.a, &pack2.b, &pack2.c, &pack2.d, &pack2.e , &pack2.f, &pack2.g);printf("\r\n4字節(jié)對(duì)齊 %x %x %x %x %x %x %x\r\n",&pack4.a, &pack4.b, &pack4.c, &pack4.d, &pack4.e , &pack4.f, &pack4.g);printf("\r\nMDK默認(rèn)對(duì)齊 %x %x %x %x %x %x %x\r\n",&pack.a, &pack.b, &pack.c, &pack.d, &pack.e , &pack.f , &pack.g);while(1){} }打印結(jié)果
1字節(jié)對(duì)齊 20005600 20005601 20005605 20005606 20005608 2000560c 2000560d2字節(jié)對(duì)齊 20005800 20005802 20005806 20005808 2000580a 2000580e 200058104字節(jié)對(duì)齊 20005a00 20005a04 20005a08 20005a0a 20005a0c 20005a10 20005a14MDK默認(rèn)對(duì)齊 20005400 20005404 20005408 2000540a 2000540c 20005410 20005418總結(jié)
以上是生活随笔為你收集整理的c语言对齐方式研究笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: freeRtos学习笔(2)任务管理
- 下一篇: freeRtos学习笔(3)临界区管理