數(shù)據(jù)的寬度與存儲,以及字節(jié)對齊策略

1. 數(shù)據(jù)的寬度與單位

計算機內(nèi)部任何數(shù)據(jù)都被表示成二進制編碼形式。二進制數(shù)據(jù)的每一位(0 or 1)二進制信息的最小單位,稱為一個"比特"(bit),簡稱"位",bit是計算機中存儲,運算和傳輸信息的最小單位。
每個西文字符需要用8個比特表示,而每個漢字需要用16個比特才能表示。計算機內(nèi)部,二進制信息的計量單位是"字節(jié)"(Byte),也成為"位組"。 1 Byte = 8 bit
計算機中運行和處理二進制信息時使用的單位除了比特和字節(jié)之外,還經(jīng)常使用"字"(word)作為單位,必須注意,不同的計算機,的長度和組成不完全相同,有的由2個字節(jié)組成,有的由4個,8個,甚至16個字節(jié)組成。


在考察計算機性能時,一個重要的指標就是機器的"字長"。平時所說的"某種機器是32位機或是64位機",其中的32,64就是指的字長。所謂機器字長通常指CPU內(nèi)部用于整數(shù)運算的數(shù)據(jù)通路的寬度,也就是說,"字長"等于CPU內(nèi)部用于整數(shù)運算的運算器位數(shù)和通用寄存器寬度。
注 : "字"和"字長"的概念不同
|- "字"用來表示被處理信息的單位,用來度量各種數(shù)據(jù)類型的寬度,大小以機器為準
|-"字長"表示數(shù)據(jù)運算,存儲和傳送的部件的寬度,它反映了計算機處理信息的一種能力。

單位換算
1 B = 8 b
K :1KB = 2^10 B = 1024 字節(jié)
M :1MB = 2^20 B
G : 1GB = 2^30 B
T : 1TB = 2^40 B


C語言中數(shù)值數(shù)據(jù)類型的寬度(字節(jié)(Byte)為單位)
類型 典型的32位機器(Linux) 64位機器
char 1 1
short int 2 2
int 4 4
long int 4 8
--- --- ---
char * 4 8
--- --- ---
float 4 4
double 8 8

另外,對于相同類型的數(shù)據(jù),并不是所有機器都采用相同的數(shù)據(jù)寬度,分配的字節(jié)數(shù)隨處理器和編譯器的不同而不同
例如:指針類型一般認為32位機器是 4 個字節(jié),64位機器是 8 個字節(jié),這個沒錯,但是在64位的機器上編譯程序,計算指針的大小,返回的是 4,這時認為是不是以前書上講的是錯的,其實不是,只不過編譯選項里的平臺是Win32,也就是在64位系統(tǒng)運行的是32位的程序,所以說是受編譯器的影響

2. 數(shù)據(jù)的儲存和排列順序

現(xiàn)代計算機基本上都采用字節(jié)編址方式,即對存儲空間的存儲單元編號時,每個地址編號中存放一個字節(jié)。

  • 例如:在一個按字節(jié)編址的計算機中,假定int型變量i的地址為0800Hi的機器數(shù)為01 23 45 67H,根據(jù)不同的地址排序方式,i的4個字節(jié)01H,23H,45H,67H有不同的排列順序
兩種排列方式:

大端模式(big endian) : 將數(shù)據(jù)的最高有效字節(jié)存放在低地址單元,最低有效字節(jié)存放在高地址單元
小端模式(small endian) : 將數(shù)據(jù)的最高有效字節(jié)存放在高地址單元,最低有效字節(jié)存放在低地址單元

字節(jié)編址地址 …… 0800H 0801H 0802H 0803H ……
大端 …… 01H 23H 45H 67H ……
小端 …… 67H 45H 23H 01H ……

補充: 網(wǎng)絡(luò)序是大端模式

Linux網(wǎng)絡(luò)編程中,為使網(wǎng)絡(luò)程序具有可移植性,使同樣的C代碼在大端和小端計算機上編譯后都能正常運行,可以調(diào)用以下庫函數(shù)做網(wǎng)絡(luò)字節(jié)序和主機字節(jié)序的轉(zhuǎn)換。

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
3. 數(shù)據(jù)對齊原則
3.1 為什么要數(shù)據(jù)對齊?

可以將存儲器看成由連續(xù)的位(cell)構(gòu)成,每8位為一個字節(jié),每個字節(jié)有一個地址編號,稱之為按字節(jié)編址,假定計算機系統(tǒng)中訪問機制限制每次訪存最多只能只能讀寫64位,即8個字節(jié),那么,第0-7字節(jié)可以同時讀寫,第8-15字節(jié)可以同時讀寫,以此類推,這種稱為8字節(jié)寬的存儲機制;因此,如果一條指令要訪問存儲器的數(shù)據(jù)不在 8i ~ 8i + 7 (i = 0,1,2 ...)之間的存儲單元內(nèi),那么就需要多次訪存,因此延長了指令的執(zhí)行時間,引入數(shù)據(jù)對齊的最重要的原因就是為了避免多次訪存帶來的指令執(zhí)行效率的降低?。?!
對齊方式下程序的執(zhí)行效率更高,為此,操作系統(tǒng)通常按照對齊方式分配管理內(nèi)存,編譯器也按照對齊方式轉(zhuǎn)換代碼。

3.2 基本數(shù)據(jù)類型的對齊策略
  • 最簡單的對齊策略:要求不同的基本類型按照其數(shù)據(jù)長度進行對齊;
    例如,int類型數(shù)據(jù)的長度為4個字節(jié),因此規(guī)定int型數(shù)據(jù)的地址是4的倍數(shù),char的長度為1個字節(jié),時刻是對齊的,其他的同理;這種策略下,對于8字節(jié)寬的存儲機制來說,所有的基本類型都僅需訪存一次;windows采用的就是這種策略
  • Linux的對齊策略 : 更加寬松,規(guī)定short數(shù)據(jù)的地址是2的倍數(shù),其他的如int ,float,double和指針等類型的數(shù)據(jù)的地址都是4的倍數(shù);
    這種情況,對于8字節(jié)寬的存儲機制,double型數(shù)據(jù)就可能需要訪存兩次
3.3 結(jié)構(gòu)體 & 結(jié)構(gòu)數(shù)組的對齊策略

滿足下面三個條件

  • (1). 結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除
    [備注]:編譯器在給結(jié)構(gòu)體開辟空間時,首先找到結(jié)構(gòu)體中最寬的基本數(shù)據(jù)類型,然后尋找內(nèi)存地址能被該基本數(shù)據(jù)類型所整除的位置,作為結(jié)構(gòu)體的首地址。將這個最寬的基本數(shù)據(jù)類型的大小作為對齊的模數(shù),也就是我們經(jīng)常說的4字節(jié)對齊(32位機器默認),8字節(jié)對齊(64位機器默認)。
  • (2). 結(jié)構(gòu)體中的每個數(shù)據(jù)成員都按照"基本數(shù)據(jù)類型對齊策略"的要求進行對齊
    也就是說:結(jié)構(gòu)體每個成員相對于結(jié)構(gòu)體首地址的偏移量(offset)都是成員自身大小的整數(shù)倍,如有需要,編譯器會在成員之間加上填充字節(jié)(internal adding)
  • (3). 結(jié)構(gòu)體的最終大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要,編譯器會在最末一個成員之后加上填充字節(jié)(trailing padding)
    也就是說:若結(jié)構(gòu)體的總大小是最大基礎(chǔ)成員大小的整數(shù)倍,那么也就一定是其他任一基礎(chǔ)成員大小的整數(shù)倍,那么每個結(jié)構(gòu)體的起始地址就一定是其任一基礎(chǔ)成員大小的整數(shù)倍 ,這樣的話就保證了所有基礎(chǔ)類型數(shù)據(jù)、非基礎(chǔ)類型數(shù)據(jù)全部對齊。

[補充] (1)(2) 是為了保證內(nèi)存對齊,提高處理器訪問內(nèi)存的效率;(3) 是為了保證結(jié)構(gòu)體數(shù)組中的每個元素都能滿足對齊要求

舉個例子:

EX1

結(jié)構(gòu)體
struct SD
{
    int i;
    short si;
    char c;
    double d;
};

按照字節(jié)對齊原則,結(jié)構(gòu)體SD所占的存儲空間大小為 16 個字節(jié)
假設(shè)結(jié)構(gòu)體的首地址為0

結(jié)構(gòu)體對齊

EX2

結(jié)構(gòu)體數(shù)組
struct SDT
{
    int i;
    short si;
    double d;
    char c;
};
struct SDT sa[10];

按照字節(jié)對齊原則,結(jié)構(gòu)體SD所占的存儲空間大小為 24 個字節(jié)
假設(shè)結(jié)構(gòu)體數(shù)組的第一個數(shù)組元素sa[0]的首地址為0

結(jié)構(gòu)體數(shù)組對齊,末尾補空



補充概念 : 首地址 n 字節(jié)對齊原則

VC對結(jié)構(gòu)的存儲的特殊處理確實提高CPU存儲變量的速度,但是有時候也帶來了一些麻煩,我們也屏蔽掉變量默認的對齊方式,自己可以設(shè)定變量的對齊方式。
VC 中提供了#pragma pack(n)來設(shè)定變量以n字節(jié)對齊方式。n字節(jié)對齊就是說變量存放的起始地址的偏移量有兩種情況:第一、如果n大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認的對齊方式,第二、如果n小于該變量的類型所占用的字節(jié)數(shù),那么偏移量為n的倍數(shù),不用滿足默認的對齊方式。結(jié)構(gòu)的總大小也有個約束條件,分下面兩種情況:如果n大于所有成員變量類型所占用的字節(jié)數(shù),那么結(jié)構(gòu)的總大小必須為占用空間最大的變量占用的空間數(shù)的倍數(shù),否則必須為n的倍數(shù)。下面舉例說明其用法。

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 國家電網(wǎng)公司企業(yè)標準(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 12,377評論 6 13
  • 你說人生如戲,云卷云舒,潮起潮落。多年以后的我們告別了高中的青澀,增添了成熟的面孔。曾經(jīng)一起背著書包,走在城市的斑...
    冰棍m閱讀 276評論 0 0
  • 傍晚的峨眉山,已經(jīng)華燈初上,天也變得暗淡起來,卻披上了淡紅色的霞衣。秀甲瀑布還在嘩嘩地沖刷著石壁,仍舊激情澎湃;摩...
    水翼閱讀 1,207評論 0 4
  • 金雨田1507閱讀 267評論 0 0

友情鏈接更多精彩內(nèi)容