在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<abbr id="27omo"></abbr>

<menu id="27omo"><dl id="27omo"></dl></menu>
    • <label id="27omo"><tt id="27omo"></tt></label>

      新聞中心

      EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > arm 結(jié)構(gòu)體對(duì)齊問(wèn)題

      arm 結(jié)構(gòu)體對(duì)齊問(wèn)題

      作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
      Arm結(jié)構(gòu)體gcc內(nèi)存邊界對(duì)齊問(wèn)題
      這段時(shí)間移植公司的linux i386程序到Arm linux平臺(tái),本以為是件工作量很小的事情,以為只要改幾個(gè)驅(qū)動(dòng)程序就OK了,沒(méi)想到在應(yīng)用程序這一塊卡了很長(zhǎng)時(shí)間。其中最煩的事情就莫過(guò)于結(jié)構(gòu)體內(nèi)存邊界對(duì)齊了。搞了這么久,終于終結(jié)了一些小經(jīng)驗(yàn)。

      默認(rèn)情況下,在32位cpu里,gcc對(duì)于結(jié)構(gòu)體的對(duì)齊方式是按照四個(gè)字節(jié)來(lái)對(duì)齊的??匆韵陆Y(jié)構(gòu)體

      typedef struct pack{
      char a;
      int b;
      short c;
      }pack;

      對(duì)于Pack結(jié)構(gòu)體,默認(rèn)情況下在arm/386平臺(tái)下(別的平臺(tái)沒(méi)試過(guò))sizeof(pack)=12,求解過(guò)程如下:

      sizeof(char)=1;

      下一個(gè)int b,由于是四個(gè)字節(jié),要求b的開(kāi)始地址從32的整數(shù)倍開(kāi)始,故需要在a后面填充3個(gè)沒(méi)用的字節(jié),記為dump(3),sizeof(b)=4,此時(shí)相當(dāng)于結(jié)構(gòu)體擴(kuò)充為

      char a;
      char dump(3);
      int b;

      看short c,現(xiàn)在c的前面有8個(gè)字節(jié),c是兩個(gè)字節(jié),c的開(kāi)始地址是從16的整數(shù)開(kāi)始,在b前面不需再加?xùn)|西.此時(shí)對(duì)于結(jié)構(gòu)體來(lái)說(shuō),sizeof(pack)=10,但是這不是最終結(jié)果,最后總的字節(jié)數(shù)也要能被4個(gè)字節(jié)整除,所以還需在short c后面再加

      dump(2);

      故總的字節(jié)數(shù)為12.

      當(dāng)然以上說(shuō)的只是簡(jiǎn)單的情況,下面談?wù)凙rm,x86在gcc里關(guān)于內(nèi)存邊界字節(jié)對(duì)齊的區(qū)別.對(duì)于同樣的結(jié)構(gòu)體,在386下

      #prama pack(1)

      后,sizeof(pack)=1 4 2=7

      而在arm下同樣的操作sizeof(pack)=1 4 2 1=8,即雖然b根a之間不要填充但總的長(zhǎng)度必須要是4的整數(shù)倍.

      在ARM 下要使結(jié)構(gòu)體按指定字節(jié)對(duì)齊,可行的方法

      1.在makefile里加-fpack-struct 選項(xiàng),這樣的話對(duì)所有的結(jié)構(gòu)按一字節(jié)對(duì)齊.

      不得不說(shuō),確實(shí)有那么些質(zhì)量較差的程序可能需要你部分自然對(duì)齊,部分一字 節(jié)對(duì)齊,此時(shí)

      2. typedef struct pack{

      }__attribute__((packed))

      可利用__attribute__屬性

      當(dāng)然最后的方式,還是自己去看ARM體系結(jié)構(gòu)與gcc編譯選項(xiàng)了。
      ------------------------------------------------------------------------------------------------------------
      淺談結(jié)構(gòu)體對(duì)齊問(wèn)題
      #include

      int main() {
      struct ms {
      double x;
      char a;
      int y;
      };
      // }__attribute__((packed));

      printf("%d/n", sizeof(struct ms));
      return 0;
      }

      linux上運(yùn)行,結(jié)果為16;如果采用注釋的那一行,則結(jié)果為13
      原文::http://dev.csdn.net/article/48/48195.shtm
      什么是內(nèi)存對(duì)齊

      考慮下面的結(jié)構(gòu):

      struct foo
      {
      char c1;
      short s;
      char c2;
      int i;
      };

      假設(shè)這個(gè)結(jié)構(gòu)的成員在內(nèi)存中是緊湊排列的,假設(shè)c1的地址是0,那么s的地址就應(yīng)該是1,c2的地址就是3,i的地址就是4。也就是
      c1 00000000, s 00000001, c2 00000003, i 00000004。

      可是,我們?cè)赩isual c/c++ 6中寫(xiě)一個(gè)簡(jiǎn)單的程序:

      struct foo a;
      printf("c1 %p, s %p, c2 %p, i %p/n",
      (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);
      運(yùn)行,輸出:
      c1 00000000, s 00000002, c2 00000004, i 00000008。

      為什么會(huì)這樣?這就是內(nèi)存對(duì)齊而導(dǎo)致的問(wèn)題。

      為什么會(huì)有內(nèi)存對(duì)齊

      以下內(nèi)容節(jié)選自《Intel Architecture 32 Manual》。
      字,雙字,和四字在自然邊界上不需要在內(nèi)存中對(duì)齊。(對(duì)字,雙字,和四字來(lái)說(shuō),自然邊界分別是偶數(shù)地址,可以被4整除的地址,和可以被8整除的地址。)
      無(wú)論如何,為了提高程序的性能,數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。原因在于,為了訪問(wèn)未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問(wèn);然而,對(duì)齊的內(nèi)存訪問(wèn)僅需要一次訪問(wèn)。
      一個(gè)字或雙字操作數(shù)跨越了4字節(jié)邊界,或者一個(gè)四字操作數(shù)跨越了8字節(jié)邊界,被認(rèn)為是未對(duì)齊的,從而需要兩次總線周期來(lái)訪問(wèn)內(nèi)存。一個(gè)字起始地址是奇數(shù)但卻沒(méi)有跨越字邊界被認(rèn)為是對(duì)齊的,能夠在一個(gè)總線周期中被訪問(wèn)。
      某些操作雙四字的指令需要內(nèi)存操作數(shù)在自然邊界上對(duì)齊。如果操作數(shù)沒(méi)有對(duì)齊,這些指令將會(huì)產(chǎn)生一個(gè)通用保護(hù)異常(#GP)。雙四字的自然邊界是能夠被16 整除的地址。其他的操作雙四字的指令允許未對(duì)齊的訪問(wèn)(不會(huì)產(chǎn)生通用保護(hù)異常),然而,需要額外的內(nèi)存總線周期來(lái)訪問(wèn)內(nèi)存中未對(duì)齊的數(shù)據(jù)。

      編譯器對(duì)內(nèi)存對(duì)齊的處理

      缺省情況下,c/c++編譯器默認(rèn)將結(jié)構(gòu)、棧中的成員數(shù)據(jù)進(jìn)行內(nèi)存對(duì)齊。因此,上面的程序輸出就變成了:
      c1 00000000, s 00000002, c2 00000004, i 00000008。
      編譯器將未對(duì)齊的成員向后移,將每一個(gè)都成員對(duì)齊到自然邊界上,從而也導(dǎo)致了整個(gè)結(jié)構(gòu)的尺寸變大。盡管會(huì)犧牲一點(diǎn)空間(成員之間有空洞),但提高了性能。
      也正是這個(gè)原因,我們不可以斷言sizeof(foo) == 8。在這個(gè)例子中,sizeof(foo) == 12。

      如何避免內(nèi)存對(duì)齊的影響

      那么,能不能既達(dá)到提高性能的目的,又能節(jié)約一點(diǎn)空間呢?有一點(diǎn)小技巧可以使用。比如我們可以將上面的結(jié)構(gòu)改成:

      struct bar
      {
      char c1;
      char c2;
      short s;
      int i;
      };
      這樣一來(lái),每個(gè)成員都對(duì)齊在其自然邊界上,從而避免了編譯器自動(dòng)對(duì)齊。在這個(gè)例子中,sizeof(bar) == 8。

      這個(gè)技巧有一個(gè)重要的作用,尤其是這個(gè)結(jié)構(gòu)作為API的一部分提供給第三方開(kāi)發(fā)使用的時(shí)候。第三方開(kāi)發(fā)者可能將編譯器的默認(rèn)對(duì)齊選項(xiàng)改變,從而造成這個(gè)結(jié)構(gòu)在你的發(fā)行的DLL中使用某種對(duì)齊方式,而在第三方開(kāi)發(fā)者哪里卻使用另外一種對(duì)齊方式。這將會(huì)導(dǎo)致重大問(wèn)題。
      比如,foo結(jié)構(gòu),我們的DLL使用默認(rèn)對(duì)齊選項(xiàng),對(duì)齊為
      c1 00000000, s 00000002, c2 00000004, i 00000008,同時(shí)sizeof(foo) == 12。
      而第三方將對(duì)齊選項(xiàng)關(guān)閉,導(dǎo)致
      c1 00000000, s 00000001, c2 00000003, i 00000004,同時(shí)sizeof(foo) == 8。

      如何使用c/c++中的對(duì)齊選項(xiàng)

      vc6中的編譯選項(xiàng)有 /Zp[1|2|4|8|16] ,/Zp1表示以1字節(jié)邊界對(duì)齊,相應(yīng)的,/Zpn表示以n字節(jié)邊界對(duì)齊。n字節(jié)邊界對(duì)齊的意思是說(shuō),一個(gè)成員的地址必須安排在成員的尺寸的整數(shù)倍地址上或者是n的整數(shù)倍地址上,取它們中的最小值。也就是:
      min ( sizeof ( member ), n)
      實(shí)際上,1字節(jié)邊界對(duì)齊也就表示了結(jié)構(gòu)成員之間沒(méi)有空洞。
      /Zpn選項(xiàng)是應(yīng)用于整個(gè)工程的,影響所有的參與編譯的結(jié)構(gòu)。
      要使用這個(gè)選項(xiàng),可以在vc6中打開(kāi)工程屬性頁(yè),c/c++頁(yè),選擇Code Generation分類(lèi),在Struct member alignment可以選擇。

      要專(zhuān)門(mén)針對(duì)某些結(jié)構(gòu)定義使用對(duì)齊選項(xiàng),可以使用#pragma pack編譯指令。指令語(yǔ)法如下:
      #pragma pack( [ show ] | [ push | pop ] [, identifier ] , n )
      意義和/Zpn選項(xiàng)相同。比如:

      #pragma pack(1)
      struct foo_pack
      {
      char c1;
      short s;
      char c2;
      int i;
      };
      #pragma pack()

      棧內(nèi)存對(duì)齊

      我們可以觀察到,在vc6中棧的對(duì)齊方式不受結(jié)構(gòu)成員對(duì)齊選項(xiàng)的影響。(本來(lái)就是兩碼事)。它總是保持對(duì)齊,而且對(duì)齊在4字節(jié)邊界上。

      驗(yàn)證代碼

      #include

      struct foo
      {
      char c1;
      short s;
      char c2;
      int i;
      };

      struct bar
      {
      char c1;
      char c2;
      short s;
      int i;
      };

      #pragma pack(1)
      struct foo_pack
      {
      char c1;
      short s;
      char c2;
      int i;
      };
      #pragma pack()

      int main(int argc, char* argv[])
      {
      char c1;
      short s;
      char c2;
      int i;

      struct foo a;
      struct bar b;
      struct foo_pack p;

      printf("stack c1 %p, s %p, c2 %p, i %p/n",
      (unsigned int)(void*)&c1 - (unsigned int)(void*)&i,
      (unsigned int)(void*)&s - (unsigned int)(void*)&i,
      (unsigned int)(void*)&c2 - (unsigned int)(void*)&i,
      (unsigned int)(void*)&i - (unsigned int)(void*)&i);

      printf("struct foo c1 %p, s %p, c2 %p, i %p/n",
      (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);

      printf("struct bar c1 %p, c2 %p, s %p, i %p/n",
      (unsigned int)(void*)&b.c1 - (unsigned int)(void*)&b,
      (unsigned int)(void*)&b.c2 - (unsigned int)(void*)&b,
      (unsigned int)(void*)&b.s - (unsigned int)(void*)&b,
      (unsigned int)(void*)&b.i - (unsigned int)(void*)&b);

      printf("struct foo_pack c1 %p, s %p, c2 %p, i %p/n",
      (unsigned int)(void*)&p.c1 - (unsigned int)(void*)&p,
      (unsigned int)(void*)&p.s - (unsigned int)(void*)&p,
      (unsigned int)(void*)&p.c2 - (unsigned int)(void*)&p,
      (unsigned int)(void*)&p.i - (unsigned int)(void*)&p);

      printf("sizeof foo is %d/n", sizeof(foo));
      printf("sizeof bar is %d/n", sizeof(bar));
      printf("sizeof foo_pack is %d/n", sizeof(foo_pack));

      return 0;
      }
      -----------------------------------------------------------------------------------------------------------在結(jié)構(gòu)中,編譯器為結(jié)構(gòu)的每個(gè)成員按其自然對(duì)界條件分配空間;各個(gè)成員按照它們被聲明的順序在內(nèi)存中順序存儲(chǔ),第一個(gè)成員的地址和整個(gè)結(jié)構(gòu)的地址相同。在缺省情況下,c編譯器為每一個(gè)變量或是數(shù)據(jù)單元按其自然對(duì)界條件分配空間

      例如,下面的結(jié)構(gòu)各成員空間分配情況

      struct test {
      char x1;
      short x2;
      float x3;
      char x4;
      };
        
                       
        結(jié)構(gòu)的第一個(gè)成員x1,其偏移地址為0,占據(jù)了第1個(gè)字節(jié)。第二個(gè)成員x2為short類(lèi)型,其起始地址必須2字節(jié)對(duì)界,因此,編譯器在x2和x1之間填充了一個(gè)空字節(jié)。結(jié)構(gòu)的第三個(gè)成員x3和第四個(gè)成員x4恰好落在其自然對(duì)界地址上,在它們前面不需要額外的填充字節(jié)。在test結(jié)構(gòu)中,成員x3要求 4字節(jié)對(duì)界,是該結(jié)構(gòu)所有成員中要求的最大對(duì)界單元,因而test結(jié)構(gòu)的自然對(duì)界條件為4字節(jié),編譯器在成員x4后面填充了3個(gè)空字節(jié)。整個(gè)結(jié)構(gòu)所占據(jù)空間為12字節(jié)。

      現(xiàn)在你知道怎么回事了吧?

      更改c編譯器的缺省分配策略
        一般地,可以通過(guò)下面的兩種方法改變?nèi)笔〉膶?duì)界條件:
        · 使用偽指令#pragma pack ([n])
        · 在編譯時(shí)使用命令行參數(shù)
      #pragma pack ([n])偽指令允許你選擇編譯器為數(shù)據(jù)分配空間所采取的對(duì)界策略:

        
                     
        
        例如,在使用了#pragma pack (1)偽指令后,test結(jié)構(gòu)各成員的空間分配情況就是按照一個(gè)字節(jié)對(duì)齊了
      #pragma pack(push) //保存對(duì)齊狀態(tài)
      #pragma pack(1)

      #pragma pack(pop)


      評(píng)論


      技術(shù)專(zhuān)區(qū)

      關(guān)閉