原文:個人我的博客 https://mengkang.net/1046.html
初中級 phper 有多久沒給本身充電了呢,安利一波個人直播 PHP 進階之路
須要字節對齊的根本緣由在於CPU訪問數據的效率問題。由於CPU每次都是從以4字節(32位CPU)或是8字節(64位CPU)的整數倍的內存地址中讀進數據的。(更深刻的緣由,誰告知下),若是不對齊的話,頗有可能一個4字節int
須要分兩次讀取。具體演示看下面的實驗。php
按各數據類型自身大小進行對齊。變量的內存地址正好位於它長度的整數倍html
#include <stdio.h> int 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\n",&a,sizeof(a)); printf("%p,sizeof(b):%lu\n",&b,sizeof(b)); printf("%p,sizeof(c):%lu\n",&c,sizeof(c)); printf("%p,sizeof(d):%lu\n",&d,sizeof(d)); printf("%p,sizeof(e):%lu\n",&e,sizeof(e)); return 0; }
輔助以圖片說明,該圖左側是上面代碼的內存圖,灰色部分表示該程序未使用的內存。右側是在上面代碼的基礎上在char a
後面聲明瞭一個short f
。
從上面的實驗和圖上咱們能夠找出如下規律:segmentfault
int
類型的變量的內存地址都是偶數(這也就是爲何鳥哥微博中說的不可能存在奇數的 int 變量的地址了);short f
地址也並無緊挨着a
,而是跟自身數據大小對齊,也就是偶數地址開始申請。反過來想,若是不對齊,好比上例中的 a,b,c 三個變量的內存地址緊挨着,而CPU每次只讀取8個字節,也就是說變量 c 還有最後一個字節沒有讀取進來。訪問數據效率就下降了。網絡
棧上各個變量申請的內存,返回的地址是這段連續內存的最小的地址。這是怎麼回事呢?框架
咱們仍是經過實驗來驗證下我上面畫的內存圖,假如我有一個int
變量,它的值佔了滿了4個字節,那麼它的四個字節裏是怎麼存放數據的,咱們用十六進制來演示0x12345678
。spa
- 爲何用一個8位的十六進制來呢?由於int 4個字節,一個字節有8位,每位有0/1兩個狀態,那麼就是2^8=256,也就是16^2。因此用了一個8位的16進制數正好能夠填滿一個 int 的內存。
- 爲何用12345678,純屬演示方便。
我先存了變量 b,而後以 char 指針 p 來依次訪問 b 的四個字節的使用狀況。.net
#include <stdio.h> int main(int argc, char const *argv[]) { char a = 1; // 0x7fff5fbff777 int b = 0x12345678; // 0x7fff5fbff770 char c = 1; // 0x7fff5fbff76f printf("%p\n",&a); printf("%p\n",&b); printf("%p\n",&c); char *p = (char *)&b; printf("%x %x %x %x\n", p[0],p[1],p[2],p[3]); // 78 56 34 12 printf("%p %p %p %p\n", &p[0],&p[1],&p[2],&p[3]); // 0x7fff5fbff770 0x7fff5fbff771 0x7fff5fbff772 0x7fff5fbff773 return 0; }
變量 b 0x12345678
的最高位是0x12
,最低位是0x78
針對實驗結果我又畫了內存圖,咱們能夠看到0x12
存放在的內存地址要比0x78
的大。指針
這裏呢就必須說明下 大小端模式code
因此,我當前的環境是小端序的形式。htm
爲何會有大端小端之分?
這個就得問硬件廠商了,都比較任性,因此歷史就這樣了。
以成員中自身對齊值最大的那個值爲標準。
int main(int argc, char const *argv[]) { struct str1{ char a; short b; int c; }; printf("sizeof(f):%lu\n",sizeof(struct str1)); struct str2{ char a; int c; short b; }; printf("sizeof(g):%lu\n",sizeof(struct str2)); struct str1 a; printf("a.a %p\n",&a.a); printf("a.b %p\n",&a.b); printf("a.c %p\n",&a.c); struct str2 b; printf("b.a %p\n",&b.a); printf("b.c %p\n",&b.c); printf("b.b %p\n",&b.b); return 0; }
結果
sizeof(f):8 sizeof(g):12 a.a 0x7fff5fbff778 a.b 0x7fff5fbff77a a.c 0x7fff5fbff77c b.a 0x7fff5fbff768 b.c 0x7fff5fbff76c b.b 0x7fff5fbff770
灰色表填充用來對齊,保證最後結構體大小是最長的成員的大小的整數倍。
實際工做中是否不按字節對齊的狀況呢?有的,好比咱們的 rpc 框架裏面進行數據傳輸的時候,會選擇設置爲緊湊型,這樣就能夠輕鬆作到跨平臺,跨語言了。
在網絡程序中採用#pragma pack(1),即變量緊縮,不但能夠減小網絡流量,還能夠兼容各類系統,不會由於系統對齊方式不一樣而致使解包錯誤。
實戰舉例 yar_header 中使用 #pragma pack(1) 和 attribute ((packed)) 的意義