日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

计算机大端模式和小端模式 内存对齐问题(sizeof)

發(fā)布時(shí)間:2023/12/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 计算机大端模式和小端模式 内存对齐问题(sizeof) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


    目錄(?)[+]

  • 一大端模式和小端模式的起源
  • 二什么是大端和小端
  • 三數(shù)組在大端小端情況下的存儲(chǔ)
  • 四為什么會(huì)有大小端模式之分呢
  • 五如何判斷機(jī)器的字節(jié)序
  • 內(nèi)存對(duì)齊問(wèn)題
  • 再講講pragma pack
  • 內(nèi)存對(duì)齊二
  • 一、大端模式和小端模式的起源

    ? ? ? ? 關(guān)于大端小端名詞的由來(lái),有一個(gè)有趣的故事,來(lái)自于Jonathan Swift的《格利佛游記》:Lilliput和Blefuscu這兩個(gè)強(qiáng)國(guó)在過(guò)去的36個(gè)月中一直在苦戰(zhàn)。戰(zhàn)爭(zhēng)的原因:大家都知道,吃雞蛋的時(shí)候,原始的方法是打破雞蛋較大的一端,可以那時(shí)的皇帝的祖父由于小時(shí)侯吃雞蛋,按這種方法把手指弄破了,因此他的父親,就下令,命令所有的子民吃雞蛋的時(shí)候,必須先打破雞蛋較小的一端,違令者重罰。然后老百姓對(duì)此法令極為反感,期間發(fā)生了多次叛亂,其中一個(gè)皇帝因此送命,另一個(gè)丟了王位,產(chǎn)生叛亂的原因就是另一個(gè)國(guó)家Blefuscu的國(guó)王大臣煽動(dòng)起來(lái)的,叛亂平息后,就逃到這個(gè)帝國(guó)避難。據(jù)估計(jì),先后幾次有11000余人情愿死也不肯去打破雞蛋較小的端吃雞蛋。這個(gè)其實(shí)諷刺當(dāng)時(shí)英國(guó)和法國(guó)之間持續(xù)的沖突。Danny Cohen一位網(wǎng)絡(luò)協(xié)議的開創(chuàng)者,第一次使用這兩個(gè)術(shù)語(yǔ)指代字節(jié)順序,后來(lái)就被大家廣泛接受。
    ?

    二、什么是大端和小端

    ? ? ? ? Big-Endian和Little-Endian的定義如下:
    1) Little-Endian就是低位字節(jié)排放在內(nèi)存的低地址端,高位字節(jié)排放在內(nèi)存的高地址端。
    2) Big-Endian就是高位字節(jié)排放在內(nèi)存的低地址端,低位字節(jié)排放在內(nèi)存的高地址端。
    舉一個(gè)例子,比如數(shù)字0x12 34 56 78在內(nèi)存中的表示形式為:

    1)大端模式:

    低地址 -----------------> 高地址
    0x12 ?| ?0x34 ?| ?0x56 ?| ?0x78

    2)小端模式:

    低地址 ------------------> 高地址
    0x78 ?| ?0x56 ?| ?0x34 ?| ?0x12

    可見(jiàn),大端模式和字符串的存儲(chǔ)模式類似。

    3)下面是兩個(gè)具體例子:

    16bit寬的數(shù)0x1234在Little-endian模式(以及Big-endian模式)CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)為:
    內(nèi)存地址 小端模式存放內(nèi)容 大端模式存放內(nèi)容
    0x4000 0x34 0x12
    0x4001 0x12 0x34

    32bit寬的數(shù)0x12345678在Little-endian模式以及Big-endian模式)CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)為:

    內(nèi)存地址 小端模式存放內(nèi)容 大端模式存放內(nèi)容
    0x4000 0x78 0x12
    0x4001 0x56 0x34
    0x4002 0x34 0x56
    0x4003 0x12 0x78

    ?4)大端小端沒(méi)有誰(shuí)優(yōu)誰(shuí)劣,各自優(yōu)勢(shì)便是對(duì)方劣勢(shì):

    小端模式 :強(qiáng)制轉(zhuǎn)換數(shù)據(jù)不需要調(diào)整字節(jié)內(nèi)容,1、2、4字節(jié)的存儲(chǔ)方式一樣。
    大端模式 :符號(hào)位的判定固定為第一個(gè)字節(jié),容易判斷正負(fù)


    三、數(shù)組在大端小端情況下的存儲(chǔ):

      以u(píng)nsigned int value = 0x12345678為例,分別看看在兩種字節(jié)序下其存儲(chǔ)情況,我們可以用unsigned char buf[4]來(lái)表示value:
      Big-Endian: 低地址存放高位,如下:
    高地址
    ? ? ? ? ---------------
    ? ? ? ? buf[3] (0x78) -- 低位
    ? ? ? ? buf[2] (0x56)
    ? ? ? ? buf[1] (0x34)
    ? ? ? ? buf[0] (0x12) -- 高位
    ? ? ? ? ---------------
    ? ? ? ? 低地址
    Little-Endian: 低地址存放低位,如下:
    高地址
    ? ? ? ? ---------------
    ? ? ? ? buf[3] (0x12) -- 高位
    ? ? ? ? buf[2] (0x34)
    ? ? ? ? buf[1] (0x56)
    ? ? ? ? buf[0] (0x78) -- 低位
    ? ? ? ? --------------

    低地址


    四、為什么會(huì)有大小端模式之分呢?

    ? ? ? 這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個(gè)地址單元都對(duì)應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為8bit。但是在C語(yǔ)言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對(duì)于位數(shù)大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如果將多個(gè)字節(jié)安排的問(wèn)題。因此就導(dǎo)致了大端存儲(chǔ)模式和小端存儲(chǔ)模式。例如一個(gè)16bit的short型x,在內(nèi)存中的地址為0x0010,x的值為0x1122,那么0x11為高字節(jié),0x22為低字節(jié)。對(duì)于大端模式,就將0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,剛好相反。我們常用的X86結(jié)構(gòu)是小端模式,而KEIL C51則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來(lái)選擇是大端模式還是小端模式。


    五、如何判斷機(jī)器的字節(jié)序

    可以編寫一個(gè)小的測(cè)試程序來(lái)判斷機(jī)器的字節(jié)序:

    [cpp] view plaincopy print?
  • BOOL?IsBigEndian()????
  • {????
  • ????int?a?=?0x1234;????
  • ????char?b?=??*(char?*)&a;??//通過(guò)將int強(qiáng)制類型轉(zhuǎn)換成char單字節(jié),通過(guò)判斷起始存儲(chǔ)位置。即等于?取b等于a的低地址部分????
  • ????if(?b?==?0x12)????
  • ????{????
  • ????????return?TRUE;????
  • ????}????
  • ????return?FALSE;????
  • }??
  • BOOL IsBigEndian() { int a = 0x1234; char b = *(char *)&a; //通過(guò)將int強(qiáng)制類型轉(zhuǎn)換成char單字節(jié),通過(guò)判斷起始存儲(chǔ)位置。即等于 取b等于a的低地址部分 if( b == 0x12) { return TRUE; } return FALSE; }

    聯(lián)合體union的存放順序是所有成員都從低地址開始存放,利用該特性可以輕松地獲得了CPU對(duì)內(nèi)存采用Little-endian還是Big-endian模式讀寫

    [cpp] view plaincopy print?
  • BOOL?IsBigEndian()????
  • {????
  • ????union?NUM????
  • ????{????
  • ????????int?a;????
  • ????????char?b;????
  • ????}num;????
  • ????num.a?=?0x1234;????
  • ????if(?num.b?==?0x12?)????
  • ????{????
  • ????????return?TRUE;????
  • ????}????
  • ????return?FALSE;????
  • }??
  • BOOL IsBigEndian() { union NUM { int a; char b; }num; num.a = 0x1234; if( num.b == 0x12 ) { return TRUE; } return FALSE; }

    內(nèi)存對(duì)齊問(wèn)題

    怎么判斷內(nèi)存對(duì)齊規(guī)則,sizeof的結(jié)果怎么來(lái)的,請(qǐng)牢記以下3條原則:(在沒(méi)有#pragma pack宏的情況下,務(wù)必看完最后一行)

    1:數(shù)據(jù)成員對(duì)齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說(shuō)是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int在32位機(jī)為4字節(jié),則要從4的整數(shù)倍地址開始存儲(chǔ)。

    2:結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ).(struct a里存有struct b,b里有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲(chǔ).)

    3:收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,.必須是其內(nèi)部最大成員的整數(shù)倍.不足的要補(bǔ)齊.

    [cpp] view plaincopy print?
  • typedef?struct?bb??
  • {??
  • ?int?id;?????????????//[0]....[3]??
  • ?double?weight;??????//[8].....[15]      原則1??
  • ?float?height;??????//[16]..[19],總長(zhǎng)要為8的整數(shù)倍,補(bǔ)齊[20]...[23]     原則3??
  • }BB;??
  • ??
  • typedef?struct?aa??
  • {??
  • ?char?name[2];?????//[0],[1]??
  • ?int??id;?????????//[4]...[7]          原則1???
  • ?double?score;?????//[8]....[15]    ??
  • ?short?grade;????//[16],[17]        ??
  • ?BB?b;?????????????//[24]......[47]       原則2??
  • }AA;??
  • ??
  • int?main()??
  • {??
  • ??AA?a;??
  • ??cout<<sizeof(a)<<"?"<<sizeof(BB)<<endl;??
  • ??return?0;??
  • }???
  • typedef struct bb {int id; //[0]....[3]double weight; //[8].....[15]      原則1float height; //[16]..[19],總長(zhǎng)要為8的整數(shù)倍,補(bǔ)齊[20]...[23]     原則3 }BB;typedef struct aa {char name[2]; //[0],[1]int id; //[4]...[7]          原則1 double score; //[8]....[15]    short grade; //[16],[17]        BB b; //[24]......[47]       原則2 }AA;int main() {AA a;cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;return 0; }

    結(jié)果是

    48 24
    ok,上面的全看明白了,內(nèi)存對(duì)齊基本過(guò)關(guān).

    再講講#pragma pack().

    在代碼前加一句#pragma pack(1),你會(huì)很高興的發(fā)現(xiàn),上面的代碼輸出為

    32 16
    bb是4+8+4=16,aa是2+4+8+2+16=32;

    這不是理想中的沒(méi)有內(nèi)存對(duì)齊的世界嗎.沒(méi)錯(cuò),#pragmapack(1),告訴編譯器,所有的對(duì)齊都按照1的整數(shù)倍對(duì)齊,換句話說(shuō)就是沒(méi)有對(duì)齊規(guī)則.

    明白了不?

    那#pragma pack(2)的結(jié)果又是多少呢?對(duì)不起,5分鐘到了,自己去測(cè)試吧.

    ps:Vc,Vs等編譯器默認(rèn)是#pragma pack(8),所以測(cè)試我們的規(guī)則會(huì)正常;注意gcc默認(rèn)是#pragma pack(4),并且gcc只支持1,2,4對(duì)齊。套用三原則里計(jì)算的對(duì)齊值是不能大于#pragma pack指定的n值。


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

    VC對(duì)結(jié)構(gòu)的存儲(chǔ)的特殊處理確實(shí)提高CPU存儲(chǔ)變量的速度,但是有時(shí)候也帶來(lái)了一些麻煩,我們也屏蔽掉變量默認(rèn)的對(duì)齊方式,自己可以設(shè)定變量的對(duì)齊方式。VC 中提供了#pragma pack(n)來(lái)設(shè)定變量以n字節(jié)對(duì)齊方式。n字節(jié)對(duì)齊就是說(shuō)變量存放的起始地址的偏移量有兩種情況:

    第一、如果n大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認(rèn)的對(duì)齊方式;

    第二、如果n小于該變量的類型所占用的字節(jié)數(shù),那么偏移量為n的倍數(shù),不用滿足默認(rèn)的對(duì)齊方式。

    結(jié)構(gòu)的總大小也有個(gè)約束條件,分下面兩種情況:如果n大于所有成員變量類型所占用的字節(jié)數(shù),那么結(jié)構(gòu)的總大小必須為占用空間最大的變量占用的空間數(shù)的倍數(shù);否則必須為n的倍數(shù)。下面舉例說(shuō)明其用法:

    [cpp] view plaincopy print?
  • #pragma?pack(push)?//保存對(duì)齊狀態(tài)???
  • #pragma?pack(4)//設(shè)定為4字節(jié)對(duì)齊???
  • struct?test???
  • {???
  • char?m1;???
  • double?m4;???
  • int?m3;???
  • };???
  • #pragma?pack(pop)//恢復(fù)對(duì)齊狀態(tài)???
  • #pragma pack(push) //保存對(duì)齊狀態(tài) #pragma pack(4)//設(shè)定為4字節(jié)對(duì)齊 struct test { char m1; double m4; int m3; }; #pragma pack(pop)//恢復(fù)對(duì)齊狀態(tài)

    以上結(jié)構(gòu)的大小為16,下面分析其存儲(chǔ)情況,首先為m1分配空間,其偏移量為0,滿足我們自己設(shè)定的對(duì)齊方式(4字節(jié)對(duì)齊),m1占用1個(gè)字節(jié)。接著開始為 m4分配空間,這時(shí)其偏移量為1,需要補(bǔ)足3個(gè)字節(jié),這樣使偏移量滿足為n=4的倍數(shù)(因?yàn)閟izeof(double)大于n),m4占用8個(gè)字節(jié)。接著為m3分配空間,這時(shí)其偏移量為12,滿足為4的倍數(shù),m3占用4個(gè)字節(jié)。這時(shí)已經(jīng)為所有成員變量分配了空間,共分配了4+8+4=16個(gè)字節(jié),滿足為n的倍數(shù)。如果把上面的#pragma pack(4)改為#pragma pack(16),那么我們可以得到結(jié)構(gòu)的大小為24。

    再看下面這個(gè)例子:

    [cpp] view plaincopy print?
  • #pragma?pack(8)??
  • struct?S1{??
  • ?char?a;??
  • ?long?b;??
  • };???
  • ??
  • struct?S2?{??
  • ?char?c;??
  • ?struct?S1?d;??
  • ?long?long?e;??
  • };??
  • #pragma?pack()??
  • #pragma pack(8) struct S1{char a;long b; }; struct S2 {char c;struct S1 d;long long e; }; #pragma pack()

    成員對(duì)齊有一個(gè)重要的條件,即每個(gè)成員分別對(duì)齊.即每個(gè)成員按自己的方式對(duì)齊.

    也就是說(shuō)上面雖然指定了按8字節(jié)對(duì)齊,但并不是所有的成員都是以8字節(jié)對(duì)齊.其對(duì)齊的規(guī)則是,每個(gè)成員按其類型的對(duì)齊參數(shù)(通常是這個(gè)類型的大小)和指定對(duì)齊參數(shù)(這里是8字節(jié))中較小的一個(gè)對(duì)齊.并且結(jié)構(gòu)的長(zhǎng)度必須為所用過(guò)的所有對(duì)齊參數(shù)的整數(shù)倍,不夠就補(bǔ)空字節(jié).

    S1中,成員a是1字節(jié)默認(rèn)按1字節(jié)對(duì)齊,指定對(duì)齊參數(shù)為8,這兩個(gè)值中取1,a按1字節(jié)對(duì)齊;成員b是4個(gè)字節(jié),默認(rèn)是按4字節(jié)對(duì)齊,這時(shí)就按4字節(jié)對(duì)齊,所以sizeof(S1)應(yīng)該為8;

    S2 中,c和S1中的a一樣,按1字節(jié)對(duì)齊,而d 是個(gè)結(jié)構(gòu),它是8個(gè)字節(jié),它按什么對(duì)齊呢?對(duì)于結(jié)構(gòu)來(lái)說(shuō),它的默認(rèn)對(duì)齊方式就是它的所有成員使用的對(duì)齊參數(shù)中最大的一個(gè),S1的就是4.所以,成員d就是 按4字節(jié)對(duì)齊.成員e是8個(gè)字節(jié),它是默認(rèn)按8字節(jié)對(duì)齊,和指定的一樣,所以它對(duì)到8字節(jié)的邊界上,這時(shí),已經(jīng)使用了12個(gè)字節(jié)了,所以又添加了4個(gè)字節(jié)的空,從第16個(gè)字節(jié)開始放置成員e.這時(shí),長(zhǎng)度為24,已經(jīng)可以被8(成員e按8字節(jié)對(duì)齊)整除.這樣,sizeof(S2)為24個(gè)字節(jié).

    這里有三點(diǎn)很重要:

    1.每個(gè)成員分別按自己的方式對(duì)齊,并能最小化長(zhǎng)度。

    2.復(fù)雜類型(如結(jié)構(gòu))的默認(rèn)對(duì)齊方式是它最長(zhǎng)的成員的對(duì)齊方式,這樣在成員是復(fù)雜類型時(shí),可以最小化長(zhǎng)度。

    3.對(duì)齊后的長(zhǎng)度必須是成員中最大的對(duì)齊參數(shù)的整數(shù)倍,這樣在處理數(shù)組時(shí)可以保證每一項(xiàng)都邊界對(duì)齊。

    ?

    轉(zhuǎn)http://blog.csdn.NET/ce123_zhouwei/article/details/6971544

    http://blog.csdn.Net/hairetz/article/details/4084088

    http://www.cnblogs.com/cpoint/p/3369456.html

    ?未整理進(jìn)來(lái) http://blog.csdn.net/fanfank/article/details/12175585

    總結(jié)

    以上是生活随笔為你收集整理的计算机大端模式和小端模式 内存对齐问题(sizeof)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。