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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

内存对齐详解

發(fā)布時(shí)間:2024/2/28 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 内存对齐详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

為什么要進(jìn)行內(nèi)存對其?

(1)?性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問。

(2) 平臺原因:不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。

(3)?空間原因:沒有進(jìn)行內(nèi)存對齊的結(jié)構(gòu)體或類會(huì)浪費(fèi)一定的空間,當(dāng)創(chuàng)建對象越多時(shí),消耗的空間越多。

?

在 C/C++ 中,結(jié)構(gòu)體/類是一種復(fù)合數(shù)據(jù)類型,其構(gòu)成元素既可以是基本數(shù)據(jù)類型(如int、long、float等)的變量,也可以是一些復(fù)合數(shù)據(jù)類型(如數(shù)組、結(jié)構(gòu)、聯(lián)合等)的數(shù)據(jù)單元。編譯器為每個(gè)成員按其自然邊界(alignment)分配空間。各個(gè)成員按照它們被聲明的順序在內(nèi)存中順序存儲,第一個(gè)成員的地址和整個(gè)結(jié)構(gòu)的地址相同。

如果一個(gè)變量的內(nèi)存地址正好位于它長度的整數(shù)倍,他就被稱做自然對齊如果在 32 位的機(jī)器下,一個(gè)int類型的地址為0x00000004,那么它就是自然對齊的。同理,short 類型的地址為0x00000002,那么它就是自然對齊的。char 類型就比較 "隨意" 了,因?yàn)樗旧黹L度就是 1 個(gè)字節(jié)。自然對其的前提下:

char?? 偏移量為sizeof(char) 即 1 的倍數(shù)? short??偏移量為sizeof(short) 即 2 的倍數(shù)? int?? 偏移量為sizeof(int) 即 4 的倍數(shù)? float??偏移量為sizeof(float) 即 4 的倍數(shù)? double?偏移量為sizeof(double) 即 8 的倍數(shù)?

?

在設(shè)置結(jié)構(gòu)體或類時(shí),不考慮內(nèi)存對齊問題,會(huì)浪費(fèi)一些空間,例如實(shí)驗(yàn)一:

struct asd1{char a;int b;short c; };//12字節(jié)struct asd2{char a;short b;int c; };//8字節(jié)

上面兩個(gè)結(jié)構(gòu)體擁有相同的數(shù)據(jù)成員 char、short 和 int,但由于各個(gè)成員按照它們被聲明的順序在內(nèi)存中順序存儲,所以不同的聲明順序?qū)е铝私Y(jié)構(gòu)體所占空間的不同。具體如下圖:

? ? ? ? ? ? ?

?

看到上面的第二張圖,有的人可能會(huì)有疑問,為什么 short 不是緊挨著 char 呢?其實(shí)這個(gè)原因在上面已經(jīng)給出了答案——自然對齊。為此,我們可以創(chuàng)建結(jié)構(gòu)體驗(yàn)證自然對齊的規(guī)則。實(shí)驗(yàn)很簡單,在原本 short 類型變量前后添加 char 類型,看結(jié)果是怎樣的。實(shí)驗(yàn)二:

struct asd3{char a;char b;short c;int d; };//8字節(jié)struct asd4{char a;short b;char cint d; };//12字節(jié)

? ? ? ? ? ? ??

?

需要注意的一點(diǎn)

當(dāng)數(shù)據(jù)成員中有 double 和 long 時(shí),情況又會(huì)有一點(diǎn)變化。還是以上面的結(jié)構(gòu)體 asd1 和 asd2 為基礎(chǔ),都添加 double 型數(shù)據(jù)成員。來看看結(jié)果是什么,實(shí)驗(yàn)三:

struct asd1{char a;int b;short c;double d; };//24個(gè)字節(jié)struct asd2{char a;short b;int c;double d; };//16個(gè)字節(jié)

只添加了一個(gè) double,但 struct asd1 的大小從 12 變到了 24。而 struct asd2 的大小從 8 變到了 16。不需要迷惑,因?yàn)檫@和 double 的自然對其有關(guān)(需要注意)。原本的 asd1 占 12 個(gè)字節(jié)大小,但是 double 對齊需要是 8 的倍數(shù),所以在 short 后面又填充了 4 個(gè)字節(jié)。此時(shí),asd1 的占 16 個(gè)字節(jié),再加上 double 的 8 個(gè)字節(jié)就成了 24 個(gè)字節(jié)。而 asd2 沒有這個(gè)問題,它原本占 8 個(gè)字節(jié)。因?yàn)檎媚軐R,所以添加 double 后占 16 個(gè)字節(jié)。具體情況如下圖所示:?

? ? ? ? ? ? ? ??? ? ?

?

?

指定對齊值

在缺省情況下,C 編譯器為每一個(gè)變量或是數(shù)據(jù)單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變?nèi)笔〉膶鐥l件:
使用偽指令 #pragma pack (n),C 編譯器將按照 n 個(gè)字節(jié)對齊。
使用偽指令 #pragma pack (),取消自定義字節(jié)對齊方式。

實(shí)驗(yàn)四:

#pragma pack(4) struct asd5{char a;int b;short c;float d;char e; };//20 #pragma pack()#pragma pack(1) struct asd6{char a;int b;short c;float d;char e; };//12 #pragma pack()

使用 #pragma pack (value)?指令將結(jié)構(gòu)體按相應(yīng)的值進(jìn)行對齊。兩個(gè)結(jié)構(gòu)體包含同樣的成員,但是卻相差 8 個(gè)字節(jié)。難道我們只需要通過簡單的指令就能完成內(nèi)存對齊的工作嗎?其實(shí)不是的。上面的對齊結(jié)果如下:

以 32 位機(jī)器為例,CPU 取的字長是 32 位。所以上面的對齊結(jié)果會(huì)這樣帶來的問題是:訪問未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問。如果我要獲取 int 和 float 的數(shù)據(jù),處理器需要訪問兩次內(nèi)存,一次獲取 "前一部分" 的值,一次獲取 "后一部分" 的值。這樣做雖然減少了空間,但是增加訪問時(shí)間的消耗。其實(shí)最理想的對齊結(jié)果應(yīng)該是:

ps.使用 #pragma pack(4) 可以讓前面的實(shí)驗(yàn)三中的 asd1 少占用 4 字節(jié)。

?

?

對齊原則

1. 數(shù)據(jù)類型自身的對齊值:對于 char 型數(shù)據(jù),其自身對齊值為1,對于 short 型為2,對于 int,float,double 類型,其自身對齊值為 4,單位字節(jié)。(上面實(shí)驗(yàn)二已經(jīng)驗(yàn)證過了)

2.?結(jié)構(gòu)體或者類的自身對齊值:其成員中自身對齊值最大的那個(gè)值。

3. 指定對齊值:#pragma pack (value) 時(shí)的指定對齊值 value。

4.?數(shù)據(jù)成員、結(jié)構(gòu)體和類的有效對齊值:自身對齊值和指定對齊值中小的那個(gè)值。

總結(jié)

以上是生活随笔為你收集整理的内存对齐详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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