摘要:棧上各個(gè)變量申請(qǐng)的內(nèi)存,返回的地址是這段連續(xù)內(nèi)存的最小的地址。為什么用一個(gè)位的十六進(jìn)制來(lái)呢因?yàn)閭€(gè)字節(jié),一個(gè)字節(jié)有位,每位有兩個(gè)狀態(tài),那么就是,也就是。為什么用,純屬演示方便。結(jié)構(gòu)體里的字節(jié)對(duì)齊以成員中自身對(duì)齊值最大的那個(gè)值為標(biāo)準(zhǔn)。
原文:我的個(gè)人博客 https://mengkang.net/1046.html鳥(niǎo)哥微博 為什么要字節(jié)對(duì)齊
初中級(jí) phper 有多久沒(méi)給自己充電了呢,安利一波我的直播 PHP 進(jìn)階之路
需要字節(jié)對(duì)齊的根本原因在于CPU訪問(wèn)數(shù)據(jù)的效率問(wèn)題。因?yàn)镃PU每次都是從以4字節(jié)(32位CPU)或是8字節(jié)(64位CPU)的整數(shù)倍的內(nèi)存地址中讀進(jìn)數(shù)據(jù)的。(更深入的原因,誰(shuí)告知下),如果不對(duì)齊的話,很有可能一個(gè)4字節(jié)int需要分兩次讀取。具體演示看下面的實(shí)驗(yàn)。
數(shù)據(jù)類型自身的對(duì)齊值按各數(shù)據(jù)類型自身大小進(jìn)行對(duì)齊。變量的內(nèi)存地址正好位于它長(zhǎng)度的整數(shù)倍
實(shí)驗(yàn)#includeint main(int argc, char const *argv[]) { char a = 1; // 0x7fff5fbff77f,sizeof(a):1 int b = 1; // 0x7fff5fbff778,sizeof(b):4 int c = 1; // 0x7fff5fbff774,sizeof(c):4 char d = 1; // 0x7fff5fbff773,sizeof(e):1 int e = 1; // 0x7fff5fbff76c,sizeof(f):4 printf("%p,sizeof(a):%lu ",&a,sizeof(a)); printf("%p,sizeof(b):%lu ",&b,sizeof(b)); printf("%p,sizeof(c):%lu ",&c,sizeof(c)); printf("%p,sizeof(d):%lu ",&d,sizeof(d)); printf("%p,sizeof(e):%lu ",&e,sizeof(e)); return 0; }
輔助以圖片說(shuō)明,該圖左側(cè)是上面代碼的內(nèi)存圖,灰色部分表示該程序未使用的內(nèi)存。右側(cè)是在上面代碼的基礎(chǔ)上在char a后面聲明了一個(gè)short f。
從上面的實(shí)驗(yàn)和圖上我們可以找出以下規(guī)律:
abcde 五個(gè)變量的內(nèi)存地址從大到下依次分配的;
如果你細(xì)看,會(huì)發(fā)現(xiàn)它們的內(nèi)存地址并不是緊密挨著的;
而且int 類型的變量的內(nèi)存地址都是偶數(shù)(這也就是為什么鳥(niǎo)哥微博中說(shuō)的不可能存在奇數(shù)的 int 變量的地址了);
再細(xì)看,發(fā)現(xiàn) int 變量的地址都是可以被4整除,所以在棧上各變量是按各數(shù)據(jù)類型自身大小進(jìn)行對(duì)齊的。
新增的short f 地址也并沒(méi)有緊挨著a,而是跟自身數(shù)據(jù)大小對(duì)齊,也就是偶數(shù)地址開(kāi)始申請(qǐng)。
棧上各個(gè)變量申請(qǐng)的內(nèi)存,返回的地址是這段連續(xù)內(nèi)存的最小的地址。
反過(guò)來(lái)想,如果不對(duì)齊,比如上例中的 a,b,c 三個(gè)變量的內(nèi)存地址緊挨著,而CPU每次只讀取8個(gè)字節(jié),也就是說(shuō)變量 c 還有最后一個(gè)字節(jié)沒(méi)有讀取進(jìn)來(lái)。訪問(wèn)數(shù)據(jù)效率就降低了。
棧上各個(gè)變量申請(qǐng)的內(nèi)存,返回的地址是這段連續(xù)內(nèi)存的最小的地址。這是怎么回事呢?
我們還是通過(guò)實(shí)驗(yàn)來(lái)驗(yàn)證下我上面畫的內(nèi)存圖,假如我有一個(gè)int變量,它的值占了滿了4個(gè)字節(jié),那么它的四個(gè)字節(jié)里是怎么存放數(shù)據(jù)的,我們用十六進(jìn)制來(lái)演示0x12345678。
為什么用一個(gè)8位的十六進(jìn)制來(lái)呢?因?yàn)閕nt 4個(gè)字節(jié),一個(gè)字節(jié)有8位,每位有0/1兩個(gè)狀態(tài),那么就是2^8=256,也就是16^2。所以用了一個(gè)8位的16進(jìn)制數(shù)正好可以填滿一個(gè) int 的內(nèi)存。
為什么用12345678,純屬演示方便。
我先存了變量 b,然后以 char 指針 p 來(lái)依次訪問(wèn) b 的四個(gè)字節(jié)的使用情況。
#includeint main(int argc, char const *argv[]) { char a = 1; // 0x7fff5fbff777 int b = 0x12345678; // 0x7fff5fbff770 char c = 1; // 0x7fff5fbff76f printf("%p ",&a); printf("%p ",&b); printf("%p ",&c); char *p = (char *)&b; printf("%x %x %x %x ", p[0],p[1],p[2],p[3]); // 78 56 34 12 printf("%p %p %p %p ", &p[0],&p[1],&p[2],&p[3]); // 0x7fff5fbff770 0x7fff5fbff771 0x7fff5fbff772 0x7fff5fbff773 return 0; }
變量 b 0x12345678的最高位是0x12,最低位是0x78
針對(duì)實(shí)驗(yàn)結(jié)果我又畫了內(nèi)存圖,我們可以看到0x12存放在的內(nèi)存地址要比0x78的大。
這里呢就必須說(shuō)明下 大小端模式
小端法(Little-Endian)就是低位字節(jié)排放在內(nèi)存的低地址端即該值的起始地址,高位字節(jié)排放在內(nèi)存的高地址端。
大端法(Big-Endian)就是高位字節(jié)排放在內(nèi)存的低地址端即該值的起始地址,低位字節(jié)排放在內(nèi)存的高地址端。
所以,我當(dāng)前的環(huán)境是小端序的形式。
為什么會(huì)有大端小端之分?結(jié)構(gòu)體里的字節(jié)對(duì)齊
這個(gè)就得問(wèn)硬件廠商了,都比較任性,所以歷史就這樣了。
以成員中自身對(duì)齊值最大的那個(gè)值為標(biāo)準(zhǔn)。
實(shí)驗(yàn)int main(int argc, char const *argv[]) { struct str1{ char a; short b; int c; }; printf("sizeof(f):%lu ",sizeof(struct str1)); struct str2{ char a; int c; short b; }; printf("sizeof(g):%lu ",sizeof(struct str2)); struct str1 a; printf("a.a %p ",&a.a); printf("a.b %p ",&a.b); printf("a.c %p ",&a.c); struct str2 b; printf("b.a %p ",&b.a); printf("b.c %p ",&b.c); printf("b.b %p ",&b.b); return 0; }
結(jié)果
sizeof(f):8 sizeof(g):12 a.a 0x7fff5fbff778 a.b 0x7fff5fbff77a a.c 0x7fff5fbff77c b.a 0x7fff5fbff768 b.c 0x7fff5fbff76c b.b 0x7fff5fbff770原理
灰色表填充用來(lái)對(duì)齊,保證最后結(jié)構(gòu)體大小是最長(zhǎng)的成員的大小的整數(shù)倍。
實(shí)際工作中是否不按字節(jié)對(duì)齊的情況呢?有的,比如我們的 rpc 框架里面進(jìn)行數(shù)據(jù)傳輸?shù)臅r(shí)候,會(huì)選擇設(shè)置為緊湊型,這樣就可以輕松做到跨平臺(tái),跨語(yǔ)言了。
在網(wǎng)絡(luò)程序中采用#pragma pack(1),即變量緊縮,不但可以減少網(wǎng)絡(luò)流量,還可以兼容各種系統(tǒng),不會(huì)因?yàn)橄到y(tǒng)對(duì)齊方式不同而導(dǎo)致解包錯(cuò)誤。
實(shí)戰(zhàn)舉例 yar_header 中使用 #pragma pack(1) 和 attribute ((packed)) 的意義
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/26255.html
目錄 前言 一、 什么是指針? 引例 計(jì)算機(jī)是怎么對(duì)內(nèi)存單元編號(hào)的呢? 內(nèi)存空間的地址如何得到 想存地址怎么辦? ? 本質(zhì)目的不是為了存地址 ?二、指針和指針類型 為什么有不同類型的指針 1.指針的解引用 2.指針+-整數(shù) 三、野指針 造成野指針的原因 1.未主動(dòng)初始化指針 ?2.指針越界訪問(wèn) 3.指針指向的空間釋放 規(guī)避野指針 四、指針運(yùn)算 1.指針+-整數(shù) ?2.指針-指針 ?3.指針的關(guān)系運(yùn)...
摘要:另外棧內(nèi)存出了作用域就會(huì)自動(dòng)釋放掉,所以不需要手動(dòng)去回收的。,其中指針變量的聲明有如下三種形式其中第一種是被推薦的寫法。數(shù)據(jù)類型 C語(yǔ)言中的基本數(shù)據(jù)類型,對(duì)于它分為兩種: 1、signed 有符號(hào)的類型,也就是支持正負(fù)號(hào)的。 2、unsigned 無(wú)符號(hào)的類型,也就是沒(méi)有負(fù)號(hào),取值從0開(kāi)始。 有符號(hào)和無(wú)符號(hào)的數(shù)據(jù)類型有啥區(qū)別呢?其實(shí)就是取值范圍不一樣,下面看一張對(duì)照表: showImg(ht...
摘要:還在上班很無(wú)聊數(shù)字華容道暢玩地址開(kāi)發(fā)源碼地址這個(gè)叫前言年末了。光隨機(jī)生成一個(gè)亂序數(shù)列是不夠的,還得保證這個(gè)數(shù)列的逆序數(shù)為偶數(shù),嗦嘎。所以,我們直接將交換的次數(shù),記為數(shù)列逆序數(shù)個(gè)數(shù),就達(dá)到了想要的效果。 還在上班?很無(wú)聊?數(shù)字華容道暢玩地址 開(kāi)發(fā)源碼地址 這個(gè)叫前言 年末了。哦,不,要過(guò)年了。以前只能一路站到公司的我,今早居然是坐著過(guò)來(lái)的。新的一年,總要學(xué)一個(gè)新東西來(lái)迎接新的未來(lái)吧,所以...
摘要:在學(xué)習(xí)語(yǔ)句的時(shí)候,對(duì)編程的基礎(chǔ)知識(shí)了解的還不是很多,或許沒(méi)有做什么太復(fù)雜的東西??梢酝ㄟ^(guò)一個(gè)內(nèi)置函數(shù)來(lái)判斷一個(gè)條件的結(jié)果還是。有朋友需要看完整教程內(nèi)容,請(qǐng)點(diǎn)擊零基礎(chǔ)學(xué),這里會(huì)及時(shí)更新,并且有完整的目錄結(jié)構(gòu),更吸納了朋友們提出的意見(jiàn)和建議。 看官是否記得,在上一部分的時(shí)候,有一講專門介紹if語(yǔ)句的:從if開(kāi)始語(yǔ)句的征程。在學(xué)習(xí)if語(yǔ)句的時(shí)候,對(duì)python編程的基礎(chǔ)知識(shí)了解的還不是很多,...
摘要:是規(guī)則的瀑布流。普通的尺寸會(huì)出現(xiàn)錯(cuò)位的問(wèn)題索引這個(gè)是右邊這個(gè)是左邊間距解決辦法,可以通過(guò)里的來(lái)判斷,這個(gè)方法不管你高度怎樣,他都是左右左右開(kāi)始排列的。 目錄介紹 01.規(guī)則瀑布流實(shí)現(xiàn)02.不規(guī)則瀑布流實(shí)現(xiàn)2.1 實(shí)現(xiàn)方式2.2 遇到問(wèn)題03.瀑布流上拉加載04.給瀑布流設(shè)置分割線05.自定義Manager崩潰06.如何避免刷新抖動(dòng)07.為何有時(shí)出現(xiàn)跳動(dòng)08.瀑布流圖片優(yōu)化09.onBi...
閱讀 2735·2021-11-11 16:54
閱讀 2369·2021-10-09 09:44
閱讀 2593·2019-08-30 15:54
閱讀 1961·2019-08-30 11:24
閱讀 1209·2019-08-29 17:03
閱讀 2135·2019-08-29 16:22
閱讀 2114·2019-08-29 13:11
閱讀 1080·2019-08-29 12:14