導航:首頁 > 操作系統 > linux結構體對齊

linux結構體對齊

發布時間:2023-03-24 09:58:34

A. 如何高效的訪問內存

影響內存訪問速度的因素主要有:
1.內存帶寬:每秒讀寫內存的數據量,由硬體配置決定。
2.CACHE高速緩沖:CPU與內存之間的緩沖器,當命中率比較高時能大大提供內存平均訪問速度。
3.TLB轉換旁視緩沖:系統虛擬地址向物理地址轉換的高速查表機制,轉換速度比普通轉換機制要快。

我們能夠優化的只有第2點和第3點。由於CACHE的小容量與SMP的同步競爭,如何最大限度的利用高速緩沖就是我們的明確優化突破口(以常用的數據結構體為例):
1.壓縮結構體大小:針對CACHE的小容量。
2.對結構體進行對齊:針對內存地址讀寫特性與SMP上CACHE的同步競爭。
3.申請地址連續的內存空間:針對TLB的小容鎮源量和CACHE命中。
4.其它優化:綜合考慮多種因素

具體優化方法
1.壓縮結構體大小
系統CACHE是有限的,並且容量很小,充分壓縮結構體大小,使得CACHE能緩存更多的被訪問數據,無非是提高內存平均訪問速度的有效方法之一。
壓縮結構體大小除了需要我們對應用邏輯做好更合理的設計,盡量去除不必要的欄位,還有一些額外針對結構體本身的壓縮方法。

1.1.對結構體欄位進行合理的排列
由於結構體自身對齊的特性,具有同樣欄位的結構體,不同的欄位排列順序會產生不同大小的結構體。

大小:12位元組
struct box_a
{
char a;
short b;
int c;

char d;
};
大小:8位元組
struct box_b
{
char a;
char d;
short b;
int c;
};

1.2.利用位域
實際中,有些結構體欄位並不需要那麼大的存儲空間,比如表示真假標記的flag欄位只取兩個值之一,0或1,此時用1個bit位即可,如果使用int類型的單一欄位就大大的浪費了空間。
示例:tcp.h
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)

__u16 res1:4,

doff:4,

fin:1,
syn:1,
rst:1,
psh:1,
ack:1,

urg:1,
ece:1,

cwr:1;

#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your defines"
#endif
__be16 window;

__sum16 check;

__be16 urg_ptr;
};

1.3.利用union
union結構體也是壓縮結構體大小的方法之一,它允許我們在某些情況下能對結構體的多個欄位進行合並或把小位元組欄位存放到大位元組欄位內。
示例:skbuff.h

struct sk_buff {



union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};

};

2.對結構體進行對御模態齊
對結構體進行對齊有兩層意思,一是指對較小結構體進行機器字對齊,二是指對較大結構體進行CACHE LINE對齊。

2.1.對較小結構體進行機器字對齊
我們知道,對於現代計算機硬體來說,內存只能通過特定的對齊地址(比如按照機器字)進行訪問。舉個例子來說,比如在64位的機器上,不管我們是要讀取第0個位元組還是要讀取第1個位元組,在硬體上傳輸的信號都是一樣的。因為它都會把地址0到地址7,這8個位元組全部讀到CPU,只是當我們是需要讀取第0個位元組時,丟掉後面7個字碼拆節,當我們是需要讀取第1個位元組,丟掉第1個和後面6個位元組。
當我們要讀取的位元組剛好落在兩個機器字內時,就出現兩次訪問內存的情況,同時通過一些邏輯計算才能得到最終的結果。
因此,為了更好的提升性能,我們須盡量將結構體做到機器字(或倍數)對齊,而結構體中一些頻繁訪問的欄位也盡量安排在機器字對齊的位置。

大小:12位元組
struct box_c
{
char a;
char d;
short b;
int c;

int e;

};
大小:16位元組
struct box_d
{
char a;

char d;

short b;
int c;

int e;

char padding[4];
};

上面表格右邊的box_d結構體,通過增加一個填充欄位padding將結構體大小增加到16位元組,從而與機器字倍數對齊,這在我們申請連續的box_d結構體數組時,仍能保證數組內的每一個結構體都與機器字倍數對齊。
通過填充欄位padding使得結構體大小與機器字倍數對齊是一種常見的做法,在linux內核源碼里隨處可見。

2.2.對較大結構體進行CACHE LINE對齊
我們知道,CACHE與內存交換的最小單位為CACHE LINE,一個CACHE LINE大小以64位元組為例。當我們的結構體大小沒有與64位元組對齊時,一個結構體可能就要佔用比原本需要更多的CACHE LINE。比如,把一個內存中沒有64位元組長的結構體緩存到CACHE時,即使該結構體本身長度或許沒有還沒有64位元組,但由於其前後搭佔在兩條CACHE LINE上,那麼對其進行淘汰時就會淘汰出去兩條CACHE LINE。
這還不是最嚴重的問題,非CACHE LINE對齊結構體在SMP機器上容易引發名為錯誤共享的CACHE問題。比如,結構體T1和T2都沒做CACHE LINE對齊,如果它們(T1後半部和T2前半部)在SMP機器上合佔了同一條CACHE,如果CPU 0對結構體T1後半部做了修改則將導致CPU 1的CACHE LINE 1失效,同樣,如果CPU 1對結構體T2前半部做了修改則也將導致CPU 0的CACHE LINE 1失效。如果CPU 0和CPU 1反復做相應的修改則導致的不良結果顯而易見。本來邏輯上沒有共享的結構體T1和T2,實際上卻共享了CACHE LINE 1,這就是所謂的錯誤共享。
Linux源碼里提供了利用GCC的__attribute__擴展屬性定義的宏來做這種對齊處理,在文件/linux-2.6.xx/include/linux/cache.h內可以找到多個相類似的宏,比如:

點擊(此處)折疊或打開
#define ____cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES)))

該宏可以用來修飾結構體欄位,作用是強制該欄位地址與CACHE LINE映射起始地址對齊。
看/linux-2.6.xx/drivers/net/e100.c內結構體nic的實現,三個____cacheline_aligned修飾欄位,表示強制這些欄位與CACHE LINE映射起始地址對齊。

點擊(此處)折疊或打開
struct nic {
/* Begin: frequently used values: keep adjacent for cache effect */
u32 msg_enable ____cacheline_aligned;
/* 4位元組空洞 */
struct net_device *netdev;
struct pci_dev *pdev;
/* 40位元組空洞 */
struct rx *rxs ____cacheline_aligned;
struct rx *rx_to_use;
struct rx *rx_to_clean;
struct rfd blank_rfd;
enum ru_state ru_running;

/* 20位元組空洞 */
spinlock_t cb_lock ____cacheline_aligned;

spinlock_t cmd_lock;

struct csr __iomem *csr;
enum scb_cmd_lo cuc_cmd;
unsigned int cbs_avail;

struct napi_struct napi;



}

回到前面的問題,如果我們對結構體T2的第一個欄位加上____cacheline_aligned修飾,則該錯誤共享即可解決。

2.3.只讀欄位和讀寫欄位隔離對齊
只讀欄位和讀寫欄位隔離對齊的目的就是為了盡量保證那些只讀欄位和讀寫欄位分別集中在CACHE的不同CACHE LINE中。由於只讀欄位幾乎不需要進行更新,因而能在CACHE中得以穩定的緩存,減少由於混合有讀寫欄位導致的對應CACHE LINE的頻繁失效問題,以便提高效率;而讀寫欄位相對集中在一起,這樣也能保證當程序讀寫結構體時,污染的CACHE LINE條數也就相對的較少。

點擊(此處)折疊或打開
typedef struct {
/* ro data */
size_t block_count; // number of total blocks
size_t meta_block_size; // sizeof per skb meta block
size_t data_block_size; // sizeof per skb data block

u8 *meta_base_addr; // base address of skb meta buffer
u8 *data_base_addr; // base address of skb data buffer
/* rw data */
size_t current_index ____cacheline_aligned; // index
} bc_buff, * bc_buff_t;

3.申請地址連續的內存空間
隨著地址空間由32位轉到64位,頁內存管理的目錄分級也越來越多,4級的目錄地址轉換也是一筆不小是開銷。硬體產商為我們提供了TLB緩沖,加速虛擬地址到物理地址的換算。但是,畢竟TLB是有限,對地址連續的內存空間進行訪問時,TLB能得到更多的命中,同時CACHE高速緩沖命中的幾率也更大。
兩段代碼,實現同一功能,但第一種方法在實際使用中,內存讀寫效率就會相對較好,特別是在申請的內存很大時(未考慮malloc異常):

點擊(此處)折疊或打開
方法一:

#define MAX 100

int i;

char *p;

struct box_d *box[MAX];

p = (char *)malloc(sizeof(struct box_d) * MAX);

for (i = 0; i < MAX; i ++)

{

box[i] = (struct box_d *)(p + sizeof(struct box_d) * i);

}

方法二:

#define MAX 100

int i;

struct box_d *box[MAX];

for (i = 0; i < MAX; i ++)

{

box[i] = (struct box_d *)malloc(sizeof(struct box_d));

}

另外,如果我們使用更大頁面(比如2M或1G)的分頁機制,同樣能夠提升性能;因為相比於原本每頁4K大小的分頁機制,應用程序申請同樣大小的內存,大頁面分頁機制需要的頁面數目更少,從而佔用的TLB項目也更少,減少虛擬地址到物理地址的轉換次數的同時,提高TLB的命中率,縮短每次轉換所需要的時間。因為大多數操作系統在分配內存時候都需要按頁對齊,所以大頁面分頁機制的缺點就是內存浪費相對比較嚴重。只有在物理內存足夠充足的情況下,大頁面分頁機制才能夠體現出優勢。

4.其它優化
4.1.預讀指令讀內存
提前預取內存中數據到CACHE內,提高CACHE的命中率,加速內存讀取速度,這是設計預讀指令的主要目的。如果當前運算復雜度比較高,那麼預取和運算就可同步進行,從而消除下一步內存訪問的時延。相應的預讀匯編指令有prefetch0、prefetch1、prefetch2、 prefetchnta。
預取指令只是給CPU一個提示,所以它可被CPU忽略,而且就算預取一段錯誤的地址也不會導致CPU異常。一般使用prefetchnta預取指令,因為它不會污染CACHE,它把每次取得的數據都存放到L2 CACHE的第一條CACHE LINE,而另外幾條指令會替換CACHE中最近最少使用的CACHE LINE。

4.2.非暫時移動指令寫內存
我們知道為了保證CACHE與內存之間的數據一致性,CPU對CACHE的寫操作主要有兩種方式同步到內存,寫透式(Write Through)和寫回式(Write-back)。不管哪種同步方式都是要消耗性能的,而在某些情況下,寫CACHE是不必要的:
有哪些情況不需要寫CACHE呢?比如做數據拷貝(高效memcpy函數實現)時,或者我們已經知道寫的數據在最近一段時間內(或者永遠)都不會再使用了,那麼此時就可以不用寫CACHE,讓對應的CACHE LINE自動失效,以便緩存其它數據。這在某些特殊場景非常有用,相應的匯編指令有movntq、movntsd、movntss、movntps、movntpd、movntdq、movntdqa。
完整的利用預讀指令和非暫時移動指令實現的高速內存拷貝函數:

點擊(此處)折疊或打開

void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size_t)

{

__asm

{

mov esi, src; //src pointer

mov edi, dest; //dest pointer

mov ebx, size_t; //ebx is our counter

shr ebx, 7; //divide by 128 (8 * 128bit registers)

loop_:

prefetchnta 128[ESI]; //SSE2 prefetch

prefetchnta 160[ESI];

prefetchnta 192[ESI];

prefetchnta 224[ESI];

movdqa xmm0, 0[ESI]; //move data from src to registers

movdqa xmm1, 16[ESI];

movdqa xmm2, 32[ESI];

movdqa xmm3, 48[ESI];

movdqa xmm4, 64[ESI];

movdqa xmm5, 80[ESI];

movdqa xmm6, 96[ESI];

movdqa xmm7, 112[ESI];

movntdq 0[EDI], xmm0; //move data from registers to dest

movntdq 16[EDI], xmm1;

movntdq 32[EDI], xmm2;

movntdq 48[EDI], xmm3;

movntdq 64[EDI], xmm4;

movntdq 80[EDI], xmm5;

movntdq 96[EDI], xmm6;

movntdq 112[EDI], xmm7;

add esi, 128;

add edi, 128;

dec ebx;

jnz loop_; //loop please

loop__end:

}

}

總結
要高效的訪問內存,必須充分利用系統CACHE的緩存功能,因為就目前來說,CACHE的訪問速度比內存快太多了。具體優化方法有:
1.用設計上壓縮結構體大小。
2.結構體盡量做到機器字(倍數)對齊。
3.結構體中頻繁訪問的欄位盡量放在機器字對齊的位置。
4.頻繁讀寫的多個結構體變數盡量同時申請,使得它們盡可能的分布在較小的線性空間范圍內,這樣可利用TLB緩沖。
5.當結構體比較大時,對結構體欄位進行初始化或設置值時最好從第一個欄位依次往後進行,這樣可保證對內存的訪問是順序進行。
6.額外的優化可以採用非暫時移動指令(如movntdq)與預讀指令(如prefetchnta)。
7.特殊情況可考慮利用多媒體指令SSE2、SSE4等。
當然,上面某些步驟之間存在沖突,比如壓縮結構體和結構體對齊,這就需要實際綜合考慮。

B. linux gcc下結構體對齊模數的問題

16的話豎斗,大概是因為lz的gcc默認是64位的編余行磨譯帶宴吧,如果是64位的話,可以看一下int的位元組數,應該是8吧,那當然是以8為模了
我的是32位的,所以對齊模數默認是4,12沒有問題

C. 結構體內存位元組對齊規律

結構體是由一批數據組合而成的一盯返種新的數據類租則殲型。組成結構型數據的每個數據稱為結構型數據的「成員」。
結構體指針的大小是8位元組,結構體的大小是根據結構體成員的大小累積計算。

內存存取粒度 :處理器並不是按位元組塊來存取內存的.它一般會以 雙位元組 , 四位元組 , 8位元組 , 16位元組 甚至 32位元組 為單位來存取內存.我們將上述這些存取單位稱為內存存取粒度。

結構體的大小是結構體中最大元素的整數倍,Student結構體中, char 的長度是 8 ,故 Student 結構體的大小為 32 ;

結構體嵌套 ,最終的大小並不是 Student2 結構體內最大的元素的倍數,而是 Student1 和 Student2 所有子元素里,最大的弊沖那一個的倍數,所以 Student2 的大小是8的倍數,按照上面的排列規則,得出結果是 53 ,但是結果必須是 8 的倍數,故最終結果是 56 。

當我們在定義結構體時,如果數據成員的定義順序安排的不合理就有可能會導致多餘內存空間的佔用和浪費。 為了達到 最佳內存空間佔用 ,系統在計算內存大小的時候,會對結構體內的元素進行重排, 按照從小到大的原則重新排列 ,下圖是 Student1 重排後的結果。

D. 操作系統中的結構體對齊,位元組對齊

1.平台原因:不是所有的硬體平台都能訪問任意地址上的任何數據;某些硬體平台智能在某些地址處取特定類型的數據,否則拋出硬體異常。

2.性能原因:數據結構(尤其是棧)應該盡可能在自然邊界上對齊,原因在於,為了訪問未對鎮知齊的內存,處理器需要作兩次內存訪問,而對齊的內存訪問僅需要一次訪問。

規則:

數據成員對齊規則:結構(struct)的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。

結構(struct)的整體對齊規則:自數據成員完成各自對齊後,結構本身也要對齊,對齊將按照#pragma pack指定的數值和結構最大數據成員長度中,比較小的那個進行

結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲

假設CPU要讀取一個4位元組大小的數據到寄存器中(假設內存讀取粒度是4)

1.數據從0位元組開始(內存對齊)

2.數據從1開始(內存不對齊)

當數據從0位元組開始的時候,直接將0-3四個位元組完全讀取到寄存器,結算完成

當數據從1開始的時候,問題很復雜,首先將前4個位元組讀到寄存器,再次讀取4-7位元組的數據進寄存器,接著把0位元組,567位元組的數據剔除,最後合並1234位元組的數據進寄存器,對一個內存未對齊的寄存器進行了這么多額外操作,大大降低了CPU的性能。

這還賣顫屬於樂觀情況(性能原因),還有平台的移植原因,因為只有部分CPU肯干,其他部分CPU遇到未對齊邊界就直接罷工了

位元組對齊:

1.第一個成員在與結構體變數偏移量(offset)為0的地址處。

2.其他成員變數要對齊到對齊數的整數倍的地址處

    (1)對齊數=對齊系數與該成員大小的較小值。

    (2)如果有宏定義 #pragma pack(n); 則它中的n 就是對齊系數。

    (3)VS中默認的對齊系數為8,linux中的默認為4.

3.結構體總大小為最大對齊數(每個成員變數除了第一個都有對齊數)的整數倍。

4.如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,

  結構體中旅敗的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。

E. 結構體內存對齊

1.什麼是內存對齊?
2.為什麼要做內存對齊?
3.結構體內存對齊規則
4.源碼內存對齊演算法

計算機內存都是以位元組為單位劃分的,從理論上講似乎對任何類型的變數的訪問可以從任何地址開始,但是實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(通常它為4或8的倍數),這就是所謂的內存對齊。內存對齊是一種在計算機內存中排列數據(表現為變數的地址) 、訪問數據(表現為CPU讀取數據)的一種方式。
內存對齊包含了兩種相互獨立又相互關聯的部分:基本數據對齊和結構體數據對齊 。

1.平台原因(移植原因):不是所有的硬體平台都能訪問任意地址上的任意數據的;某些硬體平台只能在某些地址處取某些特定類型的數據,否則拋出硬體異常。
2.性能原因:數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存襪攜訪問僅需要一次訪問。

當我們定義一個 struct 的時候,它在內存中是怎麼存儲的?佔用了多少位元組的內存空間呢?

從列印結果看出一個問題,結構體中變數相同,只是順序不同,結果影響結構體佔用內存大小,這就是iOS中內存對齊

1.數據成員對⻬規則:結構(struct)(或聯合(union))的數據成員,第
一個數據成員放在offset為0的地方,以後每個數據成員存儲的起始位置要
從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數組,
結構體等)的整數倍開始(比如int為4位元組,則要從4的整數倍地址開始存
儲。 min(當前開始的位置m,n) m=9 n=4 9 10 11 12
2.結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從
其內部最大元素大小的整數倍地址開始存儲.(struct a里存有struct b,b
里有char,int ,double等元素,那b應該從8的整數倍開始存儲.)
3.收尾工作:結構體的總大小,也就是sizeof的結果,.必須是其內部最大
成員的整數倍.不足的要補⻬。

拿上面的兩個結構體舉例說明

sizeof 最終得到的結果是該數據類型佔用空間的大小
class_getInstanceSize 獲取類的實例對象所佔用的內存大小
malloc_size 獲取系統實際分配的內存大小

演算法原理:k + 15 >> 4 << 4 ,其中 右移4 + 左移4相當於告信伏將後4位抹零,跟 k/16 * 16一樣 ,是16位元組對齊演算法,小於16就成0了

對於一個對象來說,其真正的對齊方式 是 8位元組對齊,8位元組對齊已經足夠滿足對象的需求了
apple系統為了防止一切的容錯,採用的是16位元組對齊的內存,主要是因為採用8位元組對齊時,兩個坦明對象的內存會緊挨著,顯得比較緊湊,而16位元組比較寬松,利於蘋果以後的擴展。

F. linux內存分配默認是多少位元組對齊

VC和GCC默認的都是4位元組對齊,編程中可以使用#pragma pack(n)指定對齊模數。出現以上差異的原因在於,VC和GCC中對於double類型的對齊方式不同。
Win32平台下的微軟VC編譯器在默認情況下採用如下的對齊規則: 任何基本數據類型T的對齊模數就是T的大小,即sizeof(T)。比如對於double類型(8位元組),就要求該類型數據的地址總是8的倍數,而char類型數據(1位元組)則可以從任何一個地址開始。
Linux下的GCC奉行的是另外一套規則:任何2位元組大小(包括單位元組嗎?)的數據類型(比如short)的對齊模數是2,而其它所有超過2位元組的數據類型(比如long,double)都以4為對齊模數。

復雜類型(如結構)的默認對齊方式是它最長的成員的對齊方式,這樣在成員是復雜類型時,可以最小化長度。
struct{char a;double b;}
在VC中,因為結構中存在double和char,按照最長數據類型對齊,char只佔1B,但是加上後面的double所佔空間超過8B,所以char獨佔8B;而double佔8B,一共16Byte。
在GCC中,double長度超過4位元組,按照4位元組對齊,原理同上,不過char佔4位元組,double占兩個4位元組,一共12Byte。

G. 內存對齊詳解

1、什麼是內存對齊
假設我們聲明兩個變數:

2、結構體內存對齊規則

結構體所佔用的內存與其成員在結構體中的聲明順序有關,其成員的內存對齊規則如下:

(1)每個成員分別按自己的對齊位元組數和PPB(指定的對齊位元組數,32位機默認為4)兩個位元組數最小的那個對齊,這樣可以最小化長度。

(2)復雜類型(如結構)的默認對齊方式是它最長的成員的對齊方式,這樣在成員是復雜類型時,可以最小化長度。

(3)結構體對齊後的長度必須是成員中最大的對齊參數(PPB)的整數倍,這樣在處理數組時可以保證每一項都邊界對齊。

(4)計算結構體的內存大小時,應該列出每個成員的偏移地址,則其長度=最後一個成員的偏移地址+最後一個成員數的長度+最後一個成員的調整參數(考慮PPB)。

3、案例講解:

————————————————
最後輸出的結果為:8

4、注意的問題
(1)位元組對齊取決於編譯器;
(2)一定要注意PPB大小,PPB大小由pragam pack(n)指定;
(3)結構體佔用的位元組數要能夠被PPB整除。
(4)總結出一個公式:結構體的大小等於最後一個成員的偏移量加上其大小再加上末尾的填充位元組數目,即:
sizeof( struct ) = offsetof( last item ) + sizeof( last item ) +sizeof( trailing padding )

————————————————
原文鏈接: https://blog.csdn.net/SzMinglove/java/article/details/8143056

H. 結構體內存對齊

1、數據成員對齊規則: 結構( struct )(或聯合( union )的)數據成員,第一個數據成員放在 offset 為 0 的地方,以後每個數據成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說數組,結構體等)的整數倍開始(如int為4位元組,要從4的整數倍地址開始存儲)

2、結構體作為成員: 如果一個結構體有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲。(struct a 里存有struct b, b里有 char, int ,double等元素,那b 應該從8的整數倍開始存儲。)

3、收尾工作:結構體的總大小,也就是sizeof的結果,必須是內部最大成員的整數倍,不足要補齊

( 注 :如自定義類或繼承關系 屬性的多少變化而變化,如單純的繼承NSObject並且沒有任何屬性 列印為8 因為 有隱藏 isa 萬物皆對象,而對象的本質為 繼承 objc_object 的結構體 它就是對象模板)

拓 : 我們知道alloc 流程 最重要的三部曲
cls->instanceSize:計算需要開辟的內存空間大小(這里有一個演算法為16位元組對齊)
calloc:申請內存,返回地址指針
obj->initInstanceIsa:將 類 與 isa 關聯

首先創建 3個結構體 這三個結構體內部參數類型 都一樣 只不過 是順序不一樣

根據內存規則 我們畫圖來分析

分析: ofsize 0 開始 存儲 char 類型占 一個 位元組的 a ; b 要存儲了 首先分析 自己自身多大 我是 double 類型的 我需要佔 8 個位元組 可是 如果從 a 後面 開始 不滿足自身 8 的倍數 的地方 那我只能往下數 看來只能到 8的位置開始 容納我的身體 【8 - 15 】 ; 該 c 出廠 存儲了 我自身佔用 4位元組 我是 int 類型的 我只能先從16 開始 我的天 真幸運 正好符合我 自身4位元組的倍數 我可以 存儲了 【16- 19】 d 出場了 我 自身占 2個位元組 得需要從20 位開始找 能不能滿足 我的倍數的規則要求 20 除 2 可以的 我也可以放下了 【20 - 21】
Mystruct1 結構體 承載了 21+1 個位元組 突然 大喇叭喊出規則:結構體的 總大小 ,也就是sizeof的結果, 必須是內部最大成員的整數倍,不足要補齊 :好的 那我裡面最大的是 8 位元組 那麼 我現在 算出來 22 那麼 補齊就好了嘛 不要激動 心裡默念: 二 八 16, 不滿足 ; 三八 24 滿足啦 結束了。所以 Mystruct1 佔用 24個位元組空間

分析: 從 0 的位置開始存 自身是double 類型的佔用8個位元組的 b【0-7】
a 是 佔用一個位元組的char類型 ,從 8的位置 滿足自身1位元組的倍數 所以 存儲在8的位置【8】,c來了 首先 我自己是 int類型 我佔用 4個位元組 我只能從 9的位置開始存 可是 不滿足 規則 必須是自己位元組數的倍數,那我只能往下數了 9.10.11.12 咦!12是我的位置 那就從12開始【12-15】, d來了 首先想自己是 short類型的佔用2個位元組 我只能先從 16 的位置看 咦!這么巧 正好符合規則 是我的倍數 存這沒毛病
Mystruct2 結構體 承載了 17+1 個位元組 ,大喇叭又開始廣播了:結構體的 總大小 ,也就是sizeof的結果, 必須是內部最大成員的整數倍,不足要補齊 ,好的村長 我補齊: 結構體最大的是 double占 用8個位元組 我算出來18。 2*8 = 16 不滿足 3 *8 = 24 村長 我滿足了。所以 Mystruct2 佔用 24個位元組空間

分析:從0 開始 存入一個 char類型的a d 來了 首先分析自己 是short類型 佔用2個位元組 那我如果從1 開始 那不是我的倍數 不滿足規則。所以 往下看 2 好像 是我位元組的倍數 ,好的存入 【2-3】 c來了我是 int類型的 佔4位元組 我只能從4開始存儲 好像也滿足 是我的自身4位元組的倍數 ,好的存入【4-7】,b來了 嗯我是double類型 我自身佔用8位元組 我只能從 8號位置 開始存儲 好像 8也是我的倍數 好的 存入【8-15】
`Mystruct3 結構體 承載了 15+1個位元組,行了大喇叭別廣播了 我知道了 我內部成員最大 的為8位元組 必須是 8位元組的整數倍 不足要補齊。
2*8 = 16 我滿足了 ,所以 Mystruct3佔用 16個位元組

我們已經對結構體內存很明晰了 那麼來個嵌套的

上面我們分析了 Mystruct3 佔用 16 個位元組 那麼 Mystruct4佔用多少呢

分析:
a 是佔用一個位元組的char類型 放在【0】
b是 佔4位元組的int類型 找到最近的 自身的佔用位元組的倍數的位置 為【4-7】
sturct3 是一個佔用 16位元組 的結構體 我們首先分析它最大的成員佔多少內存 這里 為double 類型 佔8個位元組 所以應該 從8 的倍數開始存放第一個元素 上面的b佔了【4-7】故只能從8往後數 正好8的位置 是 sturct3里最大元素的倍數 故第一個元素位置為【8】第二個元素為short類型佔2個位元組 所以需要從8 往後數正好滿足自身的倍數 也就是【10 11】 第三個元素為double類型 佔8個位元組 所以需要從11 往後數滿足8 的倍數 也就是【16 - 23】 因為 sturct3 是個結構體 也滿足內存對齊原則 也就是 最大元素的倍數 sturct3佔了 16位元組 正好滿足 對齊原則 a 佔1 個 空白佔3 b 佔4 個 加在一起占 24個
sturct4 內存對齊 為 最大成員的倍數 最大成員 為struct3 里double 8 位元組 所以
又是大喇叭所說的 內部成員最大位元組 的倍數
所以 sizeof(Mysturct4) 最終 為 24
注意:

I. linux下怎麼設置內存對齊

內存對齊是有規則的:
a. 基本類型:所有的基本類型都有相應的對齊參數,編譯器在編譯時,會用全局的對齊參數和當前類型的對齊參數中較小的一個進行對齊。比如,編譯時指定按8bytes對齊(用#pragma pack(8)實現之),可是由於一個char變數的大小為一個byte,所以最後還是按1byte對齊。

b. 復合類型:復合類型的對齊原則,就是取其成員變數數據類型的位元組數的最大者和在編譯時指定的對齊數兩者之間較小的位元組數進行對齊。如果沒有用諸如#pragma pack指定全局對齊數,則該復合類型的對齊數就是其成員變數數據類型位元組數之最大者。

閱讀全文

與linux結構體對齊相關的資料

熱點內容
編程語言python入門電子書 瀏覽:400
什麼是民政生存認證app 瀏覽:632
java怎麼退出 瀏覽:902
掛接pdf 瀏覽:165
浪潮伺服器遠程修改bmc地址 瀏覽:119
單立文電影 瀏覽:873
linuxtar命令 瀏覽:876
優酷app如何屏蔽短視頻 瀏覽:480
看著就解壓的軟體 瀏覽:785
android撥號界面源碼 瀏覽:54
在線高清 瀏覽:375
程序員陸漓發型劉海 瀏覽:484
台灣電影《三十兒立》 瀏覽:315
南京學程序員培訓班 瀏覽:267
怎麼把華為手機app密碼刪掉 瀏覽:623
android事件冒泡 瀏覽:685
大片在線高清電影 瀏覽:791
程序員限制年齡怎麼辦 瀏覽:428
ug80數控編程視頻教程下載 瀏覽:771
四川信息化少兒編程定製 瀏覽:9