国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
C語(yǔ)言結(jié)構(gòu)體對(duì)齊

摘要:最近有粉絲說(shuō)在筆試的時(shí)候,經(jīng)常遇到求結(jié)構(gòu)體字節(jié)數(shù)的問(wèn)題,做完后不知道自己寫(xiě)對(duì)了沒(méi)。這篇文章就來(lái)介紹一下結(jié)構(gòu)體對(duì)齊的計(jì)算方法。不知道你們筆試的時(shí)候有沒(méi)有遇到這種題目呢?

一、字節(jié)對(duì)齊的基本概念

1.1 什么是字節(jié)對(duì)齊

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

為了使CPU能夠?qū)ψ兞窟M(jìn)行快速的訪問(wèn),變量的起始地址應(yīng)該具有某些特性,即所謂的”對(duì)齊”。比如4字節(jié)的int型,其起始地址應(yīng)該位于4字節(jié)的邊界上,即起始地址能夠被4整除。

1.2 為什么需要字節(jié)對(duì)齊

當(dāng)我們?cè)贑語(yǔ)言中定義結(jié)構(gòu)體時(shí),編譯器會(huì)對(duì)結(jié)構(gòu)體的成員進(jìn)行內(nèi)存對(duì)齊,以提高訪問(wèn)效率和節(jié)約內(nèi)存。如果沒(méi)有對(duì)齊的話,CPU在取數(shù)的時(shí)候,會(huì)花更多的指令周期。

一個(gè)32位系統(tǒng),假設(shè)有個(gè)整型變量的地址不是自然對(duì)齊,比如為0x00000002,則CPU取它的值需要訪問(wèn)兩次內(nèi)存,第一次取從0x00000002-0x00000003的一個(gè)short,第二次取從0x00000004-0x00000005的一個(gè)short,然后組合得到所要的數(shù)據(jù);如果變量在0x00000003地址上的話則要訪問(wèn)三次內(nèi)存,第一次為char,第二次為short,第三次為char,然后組合得到整型數(shù)據(jù)。而如果變量在自然對(duì)齊位置上,則只要訪問(wèn)一次就可以取出數(shù)據(jù)

1.3 結(jié)構(gòu)體對(duì)齊的規(guī)則

1,結(jié)構(gòu)體的第一個(gè)成員永遠(yuǎn)放在結(jié)構(gòu)體起始位置偏移為0的地址

2,結(jié)構(gòu)體從第二個(gè)成員,總是放在一個(gè)對(duì)齊數(shù)的整數(shù)倍數(shù)

   對(duì)齊數(shù) = 編譯器默認(rèn)的對(duì)齊數(shù)和變量自身大小的較小值

3,結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(每個(gè)成員變量都有一個(gè)對(duì)齊數(shù))的整數(shù)倍。

4,如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。

二、結(jié)構(gòu)體對(duì)齊的計(jì)算

2.1 例子一

先來(lái)看一下這個(gè)結(jié)構(gòu)體占了幾個(gè)字節(jié),說(shuō)明一下,下面的例子都是在32bit系統(tǒng)上運(yùn)行的

#include <stdio.h>#include <stddef.h>

int main(void){ typedef struct { char c1; int i; char c2; }MyStruct; MyStruct st; printf('%d\r\n', sizeof(MyStruct)); printf('offset c1:%d, i:%d, c2:%d\r\n', offsetof(MyStruct, c1), offsetof(MyStruct, i), offsetof(MyStruct, c2)); printf('addr c1:%x, i:%x, c2:%x\r\n', &st.c1, &st.i, &st.c2);

return 0; }

運(yùn)行結(jié)果,這個(gè)結(jié)構(gòu)體占12個(gè)字節(jié)

先來(lái)介紹一下offsetof()這個(gè)宏,如果想知道結(jié)構(gòu)體的某個(gè)成員相對(duì)于結(jié)構(gòu)體的首地址的偏移量,可通過(guò)這個(gè)宏獲取,這個(gè)宏在頭文件stddef.h中。我們看到結(jié)構(gòu)體中的成員c1,i,c2分別相對(duì)于結(jié)構(gòu)體的首地址偏移0,4,8

解釋:c1按1字節(jié)對(duì)齊,但i為int類型,按4字節(jié)對(duì)齊,所以不能緊跟其后,i的地址要為4的整數(shù)倍,所以在c1后空出了3字節(jié)開(kāi)始存放,c2為1字節(jié)對(duì)齊,緊跟在i后面即可,這樣算的話,總字節(jié)數(shù)為9,但結(jié)構(gòu)體的總大小要為最大對(duì)齊數(shù)的整數(shù)倍,這個(gè)結(jié)構(gòu)體的最大對(duì)齊數(shù)就是4,所以得在c2的后面再補(bǔ)3個(gè)字節(jié),所以這個(gè)結(jié)構(gòu)體就占用了12字節(jié)。

假設(shè)下圖左邊那一列是變量存放的地址,右邊是存放的變量

如果將上面例子的結(jié)構(gòu)體成員換一下位置,結(jié)果又是怎樣的呢?

#include <stdio.h>#include <stddef.h>
int main(void){ typedef struct { char c1; char c2; int i; }MyStruct; MyStruct st; printf('%d\r\n', sizeof(MyStruct)); printf('offset c1:%d, c2:%d, i:%d\r\n', offsetof(MyStruct, c1), offsetof(MyStruct, c2), offsetof(MyStruct, i)); printf('addr c1:%x, c2:%x, i:%x\r\n', &st.c1, &st.c2, &st.i); return 0; }

運(yùn)行結(jié)果:

解釋:c1和c2分別按1字節(jié)對(duì)齊,所以c2緊跟c1后面,i按4字節(jié)對(duì)齊,所以空2個(gè)字節(jié),再存放i。那么整個(gè)結(jié)構(gòu)體大小為8字節(jié),也滿足是最大對(duì)齊數(shù)的整數(shù)倍。

實(shí)際上,這兩個(gè)例子不管是32bit還是64bit的系統(tǒng),結(jié)果都是一樣的,因?yàn)閏har類型和int類型在32bit和64bit系統(tǒng)中,占用的空間是一樣的。當(dāng)然了,最好是在使用前先用sizeof測(cè)一下每種類型在當(dāng)前環(huán)境中占用的大小。

因此,在實(shí)際項(xiàng)目開(kāi)發(fā)中,如果定義的結(jié)構(gòu)體有很多成員,盡可能地把同類型的成員放在一起,這樣可以節(jié)省一些空間。

2.2 例子二

#include <stdio.h>#include <stddef.h>
int main(void){ typedef struct { char c1; short s1; char c2; int i; char c3; }MyStruct; MyStruct st; printf('%d\r\n', sizeof(MyStruct)); printf('offset c1:%d, s1:%d, c2:%d, i:%d, c3:%d\r\n', offsetof(MyStruct, c1), offsetof(MyStruct, s1), offsetof(MyStruct, c2), offsetof(MyStruct, i), offsetof(MyStruct, c3)); printf('addr c1:%x, s1:%x, c2:%x, i:%x, c3:%x\r\n', &st.c1, &st.s1, &st.c2, &st.i, &st.c3); return 0; }

運(yùn)行結(jié)果:

解釋:c1為1字節(jié)對(duì)齊,s1為2字節(jié)對(duì)齊,地址要為2的整數(shù)倍,所以不能緊跟c1后面,得從2開(kāi)始,c2是1字節(jié)對(duì)齊,i為4字節(jié)對(duì)齊,不能從地址5開(kāi)始存放,否則CPU訪問(wèn)的時(shí)候需要很多次,甚至可能會(huì)出錯(cuò)。所以要在c2后空出3字節(jié),i從地址8開(kāi)始,c3為1字節(jié)對(duì)齊,緊跟其后即可,累加起來(lái)為13個(gè)字節(jié),結(jié)構(gòu)體總大小又要為最大對(duì)齊數(shù)整數(shù)倍,所以該結(jié)構(gòu)體大小為16

那么,將同種類型的成員都放在一起,又占用了多少空間呢?

實(shí)際結(jié)果:

2.3 例子三

看一下帶結(jié)構(gòu)體嵌套的如何計(jì)算:

#include <stdio.h>#include <stddef.h>
int main(void){ typedef struct { int j; char c; }MyS1; typedef struct { char c1; MyS1 my_s1; short s1; int i; }MyStruct; MyStruct st; printf('%d\r\n', sizeof(MyStruct)); printf('offset c1:%d, my_s1:%d, s1:%d, i:%d\r\n', offsetof(MyStruct, c1), offsetof(MyStruct, my_s1), offsetof(MyStruct, s1), offsetof(MyStruct, i)); printf('addr c1:%x, my_s1:%x, s1:%x, i:%x\r\n', &st.c1, &st.my_s1, &st.s1, &st.i); return 0; }

解釋:看了前面幾個(gè)例子的分析,相信這個(gè)結(jié)構(gòu)體嵌套的大家也會(huì),原理是一樣的。c1為1字節(jié)對(duì)齊,嵌套的結(jié)構(gòu)體my_s1中的 j 為4字節(jié)對(duì)齊,地址要為4的整數(shù)倍,所以c1后要空出3個(gè)字節(jié),c為1個(gè)字節(jié),緊跟 j 后,s1為2字節(jié),在c后面空出2個(gè)字節(jié),i 是4個(gè)字節(jié),s1后面再空2個(gè)字節(jié)保持對(duì)齊,這樣的話,就是 4+4+2+2+4=16,最大對(duì)齊數(shù)是4,16也是4的整數(shù)倍。因此,這個(gè)結(jié)構(gòu)體大小為16字節(jié)。有沒(méi)有很多同學(xué)會(huì)犯這個(gè)錯(cuò)誤呢?

不要忽略了嵌套結(jié)構(gòu)體的自身的對(duì)齊,嵌套的結(jié)構(gòu)體my_s1的最大對(duì)齊數(shù)為4,因此嵌套的結(jié)構(gòu)體my_s1的結(jié)構(gòu)體大小要為4的整數(shù)倍,所以my_s1的結(jié)構(gòu)體大小為8字節(jié),所以,這個(gè)結(jié)構(gòu)體的大小為20字節(jié)

運(yùn)行結(jié)果:

那么將嵌套結(jié)構(gòu)體中的int類型改成double類型,又是多少呢?

#include <stdio.h>#include <stddef.h>
int main(void){ typedef struct { double j; char c; }MyS1; typedef struct { char c1; MyS1 my_s1; short s1; int i; }MyStruct; MyStruct st; printf('%d\r\n', sizeof(MyStruct)); printf('offset c1:%d, my_s1:%d, s1:%d, i:%d\r\n', offsetof(MyStruct, c1), offsetof(MyStruct, my_s1), offsetof(MyStruct, s1), offsetof(MyStruct, i)); printf('addr c1:%x, my_s1:%x, s1:%x, i:%x\r\n', &st.c1, &st.my_s1, &st.s1, &st.i); return 0; }

運(yùn)行結(jié)果:

這個(gè)我就不帶著大家一步一步算了,自己算一下,看下你學(xué)會(huì)了沒(méi)

2.4 例子四

看一下帶聯(lián)合體嵌套的如何計(jì)算

#include <stdio.h>#include <stddef.h>
int main(void){ typedef struct { char c1; union { int j; char c[8]; }MyUnion; short s1; int i; }MyStruct; MyStruct st; printf('%d\r\n', sizeof(MyStruct)); printf('offset c1:%d, MyUnion:%d, s1:%d, i:%d\r\n', offsetof(MyStruct, c1), offsetof(MyStruct, MyUnion), offsetof(MyStruct, s1), offsetof(MyStruct, i)); printf('addr c1:%x, MyUnion:%x, s1:%x, i:%x\r\n', &st.c1, &st.MyUnion, &st.s1, &st.i); return 0; }

解釋:這里要注意一點(diǎn)就是,聯(lián)合體的各成員共用一塊內(nèi)存空間,并且同時(shí)只有一個(gè)成員可以得到這塊內(nèi)存的使用權(quán)(對(duì)該內(nèi)存的讀寫(xiě)),各變量共用一個(gè)內(nèi)存首地址。

運(yùn)行結(jié)果:

個(gè)人覺(jué)得也不用刻意去記這些規(guī)則吧,重在理解,記規(guī)則的話,很久沒(méi)用了估計(jì)就忘了。多看看,多筆算算就清楚了。看了前面的分析,其實(shí)就兩點(diǎn),第一個(gè)就是結(jié)構(gòu)體成員變量存放的地址是該變量類型的整數(shù)倍;第二點(diǎn)就是結(jié)構(gòu)體的大小為最大對(duì)齊數(shù)的整數(shù)倍。

三、修改對(duì)齊方式

3.1 使用偽指令#pragma pack (n)

如果你不想使用編譯器的默認(rèn)對(duì)齊方式,可通過(guò)以下方式修改結(jié)構(gòu)體的字節(jié)對(duì)齊方式

在定義的結(jié)構(gòu)體前后加上這兩條指令,n表示你想讓這個(gè)結(jié)構(gòu)體按照幾字節(jié)對(duì)齊。

· 使用偽指令#pragma pack (n),C編譯器將按照n個(gè)字節(jié)對(duì)齊。
·
使用偽指令#pragma pack (),取消自定義字節(jié)對(duì)齊方式。

#include <stdio.h>#include <stddef.h>
int main(void){ #pragma pack (1) typedef struct { char c1; short s1; int i; }MyStruct; #pragma pack () MyStruct st; printf('%d\r\n', sizeof(MyStruct)); printf('offset c1:%d, s1:%d, i:%d\r\n', offsetof(MyStruct, c1), offsetof(MyStruct, s1), offsetof(MyStruct, i)); printf('addr c1:%x, s1:%x, i:%x\r\n', &st.c1, &st.s1, &st.i); return 0; }

運(yùn)行結(jié)果:

因?yàn)橐呀?jīng)設(shè)定了按1字節(jié)對(duì)齊,所以就是,c1為1字節(jié),s1為2字節(jié),i 為2字節(jié),加起來(lái)就是 7 字節(jié)。

改成按2字節(jié)對(duì)齊又是多少呢?

實(shí)際就是c1后面再空了一個(gè)字節(jié)

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
ANSI C中取得結(jié)構(gòu)體字段偏移量的常用方法
這種求結(jié)構(gòu)體成員大小的方法你可能還沒(méi)掌握~
C語(yǔ)言的那些小秘密之字節(jié)對(duì)齊
typedef struct與struct的區(qū)別
container
typedef
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服