欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

嵌入式C語言自我修養(yǎng)07:地址對(duì)齊那些事兒-創(chuàng)新互聯(lián)

7.1 屬性聲明:aligned

GNU C 通過 attribute 來聲明 aligned 和 packed 屬性,指定一個(gè)變量或類型的對(duì)齊方式。這兩個(gè)屬性用來告訴編譯器:在給變量分配存儲(chǔ)空間時(shí),要按指定的地址對(duì)齊方式給變量分配地址。如果你想定義一個(gè)變量,在內(nèi)存中以8字節(jié)地址對(duì)齊,就可以這樣定義。

雙城ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來市場(chǎng)廣闊!成為成都創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!
int a __attribute__((aligned(8));

通過 aligned 屬性,我們可以直接顯式指定變量 a 在內(nèi)存中的地址對(duì)齊方式。aligned 有一個(gè)參數(shù),表示要按幾字節(jié)對(duì)齊,使用時(shí)要注意地址對(duì)齊的字節(jié)數(shù)必須是2的冪次方,否則編譯就會(huì)出錯(cuò)。

什么是數(shù)據(jù)對(duì)齊

一般情況下,當(dāng)我們定義一個(gè)變量,編譯器會(huì)按照默認(rèn)的地址對(duì)齊方式,來給該變量分配一個(gè)存儲(chǔ)空間地址。如果該變量是一個(gè) int 型數(shù)據(jù),那么編譯器就會(huì)按4字節(jié)或4字節(jié)的整數(shù)倍對(duì)齊;如果該變量是一個(gè) short 型數(shù)據(jù),那么編譯器就會(huì)按2字節(jié)或2字節(jié)的整數(shù)倍邊界對(duì)齊;如果是一個(gè) char 類型的變量,那么編譯器就會(huì)按照1字節(jié)對(duì)齊。

 int a = 1;
 int b = 2;
 char c1 = 3;
 char c2 = 4;
 int main(void)
 {
     printf("a: %p\n",&a);
     printf("b: %p\n",&b);
     printf("c1:%p\n",&c1);
     printf("c2:%p\n",&c2);
     return 0;
 }

在上面的程序中,我們分別定義2個(gè) int 型變量,2個(gè) char 型變量,然后分別打印它們的地址,運(yùn)行結(jié)果如下。

a:  00402000
b:  00402004
c1: 00402008
c2: 00402009

通過運(yùn)行結(jié)果我們可以看到,對(duì)于 int 型數(shù)據(jù),其在內(nèi)存中的地址都是以4字節(jié)或4字節(jié)整數(shù)倍對(duì)齊的。而 char 類型的數(shù)據(jù),其在內(nèi)存中是以1字節(jié)對(duì)齊的。變量 c2 就直接分配到了 c1 變量的下一個(gè)存儲(chǔ)單元,不用像 int 數(shù)據(jù)那樣考慮4字節(jié)對(duì)齊。接下來,我們修改一下程序,指定變量 c2 按4字節(jié)對(duì)齊。

 int a = 1;
 int b = 2;
 char c1 = 3;
 char c2 __attribute__((aligned(4))) = 4;
 int main(void)
 {
     printf("a: %p\n",&a);
     printf("b: %p\n",&b);
     printf("c1:%p\n",&c1);
     printf("c2:%p\n",&c2);
     return 0;
 }

運(yùn)行結(jié)果如下。

a:  00402000
b:  00402004
c1: 00402008
c2: 0040200C

通過運(yùn)行結(jié)果可以看到,字符變量 c2 由于使用 aligned 屬性聲明按照4字節(jié)邊界對(duì)齊,所以編譯器不可能再給其分配 0x00402009 這個(gè)地址,因?yàn)檫@個(gè)地址不是4字節(jié)對(duì)齊的。編譯器空出3個(gè)字節(jié)單元,直接從 0x0040200C 這個(gè)地址上給變量 c2 分配存儲(chǔ)空間。

為什么要數(shù)據(jù)對(duì)齊?

通過 aligned 這個(gè)屬性聲明,我們雖然可以顯式指定變量的地址對(duì)齊方式,但是也會(huì)因邊界對(duì)齊造成一定的內(nèi)存空洞,浪費(fèi)一定的內(nèi)存空間。比如在上面這個(gè)程序中,0x00402009~0x0040200b 這三個(gè)地址空間的存儲(chǔ)單元就沒有被使用。

既然地址對(duì)齊會(huì)造成一定的內(nèi)存空洞,那我們?yōu)槭裁催€要按照這種對(duì)齊方式去存儲(chǔ)數(shù)據(jù)呢?一個(gè)主要原因就是,這種對(duì)齊設(shè)置可以簡(jiǎn)化 CPU 和內(nèi)存 RAM 之間的接口和硬件設(shè)計(jì)。比如一個(gè)32位的計(jì)算機(jī)系統(tǒng),CPU 讀取內(nèi)存時(shí),硬件設(shè)計(jì)上可能只支持4字節(jié)或4字節(jié)倍數(shù)對(duì)齊的地址訪問,CPU 每次往內(nèi)存 RAM 讀寫數(shù)據(jù)時(shí),一個(gè)周期可以讀寫4個(gè)字節(jié)。如果我們把一個(gè)數(shù)據(jù)放在4字節(jié)對(duì)齊的地址上,那么CPU一次就可以把數(shù)據(jù)讀寫完畢;如果我們把一個(gè) int 型數(shù)據(jù)放在一個(gè)非4字節(jié)對(duì)齊的地址上,那 CPU 就要分2次才能把這個(gè)4字節(jié)大小的數(shù)據(jù)讀寫完畢。

為了配合計(jì)算機(jī)的硬件設(shè)計(jì),編譯器在編譯程序時(shí),對(duì)于一些基本數(shù)據(jù)類型,比如 int、char、short、float 等,會(huì)按照其數(shù)據(jù)類型的大小進(jìn)行地址對(duì)齊,按照這種地址對(duì)齊方式分配的存儲(chǔ)地址,CPU 一次就可以讀寫完畢。雖然邊界對(duì)齊會(huì)造成一些內(nèi)存空洞,浪費(fèi)一些內(nèi)存單元,但是在硬件上的設(shè)計(jì)卻大大簡(jiǎn)化了。這也是編譯器給我們定義的變量分配地址時(shí),不同類型變量按不同字節(jié)數(shù)地址對(duì)齊的原因。

除了 int、char、short、float 這些基本類型數(shù)據(jù),對(duì)于一些復(fù)合類型數(shù)據(jù),也要滿足地址對(duì)齊要求。

7.2 結(jié)構(gòu)體的對(duì)齊

結(jié)構(gòu)體作為一種復(fù)合數(shù)據(jù)類型,編譯器在給一個(gè)結(jié)構(gòu)體變量分配存儲(chǔ)空間時(shí),不僅要考慮結(jié)構(gòu)體內(nèi)各個(gè)基本成員的地址對(duì)齊,還要考慮結(jié)構(gòu)體整體的對(duì)齊。為了結(jié)構(gòu)體內(nèi)的成員地址對(duì)齊,編譯器可能會(huì)在結(jié)構(gòu)體內(nèi)填充一些空間;為了結(jié)構(gòu)體整體對(duì)齊,編譯器可能會(huì)在結(jié)構(gòu)體的末尾填充一些空間。

接下來,我們定義一個(gè)結(jié)構(gòu)體,結(jié)構(gòu)體內(nèi)定義 int、char 和 short 三種成員,并打印結(jié)構(gòu)體的大小和各個(gè)成員的地址。

 struct data{
     char a;
     int b ;
     short c ;
 }
 int main(void)
 {
     struct data s;
     printf("size:%d\n",sizeof(s));
     printf("a:%p\n",&s.a);
     printf("b:%p\n",&s.b);
     printf("c:%p\n",&s.c);
 }

程序運(yùn)行結(jié)果如下。

size: 12
 &s.a: 0028FF30
 &s.b: 0028FF34
 &s.c: 0028FF38

我們可以看到,因?yàn)榻Y(jié)構(gòu)體的成員 b 需要4字節(jié)對(duì)齊,編譯器在給成員 a 分配完空間后,接著會(huì)空出3個(gè)字節(jié),在滿足4字節(jié)對(duì)齊的 0x0028FF34 地址處才給成員 b 分配存儲(chǔ)空間。接著是 short 類型的成員 c 占據(jù)2字節(jié)的存儲(chǔ)空間。三個(gè)結(jié)構(gòu)體成員一共占據(jù)4+4+2=10字節(jié)的存儲(chǔ)空間,根據(jù)結(jié)構(gòu)體的對(duì)齊規(guī)則,結(jié)構(gòu)體的整體對(duì)齊要向結(jié)構(gòu)體所有成員中大對(duì)齊字節(jié)數(shù)或其整數(shù)倍對(duì)齊,或者說結(jié)構(gòu)體的整體長(zhǎng)度要為其大成員字節(jié)數(shù)的整數(shù)倍,如果不是整數(shù)倍要補(bǔ)齊。因?yàn)榻Y(jié)構(gòu)體大成員 int 為4個(gè)字節(jié),或者說按4字節(jié)的整數(shù)倍對(duì)齊,所以結(jié)構(gòu)體的長(zhǎng)度要為4的整數(shù)倍,要在結(jié)構(gòu)體的末尾補(bǔ)充2個(gè)字節(jié),所以最后結(jié)構(gòu)體的 size 為12個(gè)字節(jié)。

結(jié)構(gòu)體成員中,不同的排放順序,可能也會(huì)導(dǎo)致結(jié)構(gòu)體的整體長(zhǎng)度不一樣,我們修改一下上面的程序。

struct data{
     char a;
     short b ;
     int c ;
 };
 int main(void)
 {
     struct data s;
     printf("size: %d\n",sizeof(s));
     printf("&s.a: %p\n",&s.a);
     printf("&s.b: %p\n",&s.b);
     printf("&s.c: %p\n",&s.c);
 }

程序運(yùn)行結(jié)果如下。

size: 8
 &s.a: 0028FF30
 &s.b: 0028FF32
 &s.c: 0028FF34

我們調(diào)整了一些成員順序,你會(huì)發(fā)現(xiàn),char 型變量 a 和 short 型變量 b,分配在了結(jié)構(gòu)體的前4個(gè)字節(jié)存儲(chǔ)空間中,而且都滿足各自的地址對(duì)齊,整個(gè)結(jié)構(gòu)體大小是8字節(jié),只造成一個(gè)字節(jié)的內(nèi)存空洞。我們繼續(xù)修改程序,讓 short 型的變量 b 按4字節(jié)對(duì)齊:

struct data{
     char a;
     short b __attribute__((aligned(4)));
     int c ;
 };

程序運(yùn)行結(jié)果如下。

 size: 12
 &s.a: 0028FF30
 &s.b: 0028FF34
 &s.c: 0028FF38

你會(huì)發(fā)現(xiàn),結(jié)構(gòu)體的大小又重新變?yōu)?2個(gè)字節(jié)。這是因?yàn)椋覀冿@式指定 short 變量以4字節(jié)地址對(duì)齊,導(dǎo)致變量 a 的后面填充了3個(gè)字節(jié)空間。int 型變量 c 也要4字節(jié)對(duì)齊,所以變量 b 的后面也填充了2個(gè)字節(jié),導(dǎo)致整個(gè)結(jié)構(gòu)體的大小為12字節(jié)。

我們不僅可以顯式指定結(jié)構(gòu)體內(nèi)某個(gè)成員的地址對(duì)齊,也可以指定整個(gè)結(jié)構(gòu)體的對(duì)齊方式。

struct data{
     char a;
     short b;
     int c ;
 }__attribute__((aligned(16)));

程序運(yùn)行結(jié)果如下。

 size: 16
 &s.a: 0028FF30
 &s.b: 0028FF32
 &s.c: 0028FF34

在這個(gè)結(jié)構(gòu)體中,各個(gè)成員一共占8個(gè)字節(jié)。通過前面學(xué)習(xí)我們知道,整個(gè)結(jié)構(gòu)體的對(duì)齊只要是大成員對(duì)齊字節(jié)數(shù)的整數(shù)倍即可。所以這個(gè)結(jié)構(gòu)體整體就以8字節(jié)對(duì)齊,結(jié)構(gòu)體的整體長(zhǎng)度為8字節(jié)。但是我們?cè)谶@里,顯式指定結(jié)構(gòu)體整體以16字節(jié)對(duì)齊,所以編譯器就會(huì)在這個(gè)結(jié)構(gòu)體的末尾填充8個(gè)字節(jié)以滿足16字節(jié)對(duì)齊的要求,導(dǎo)致結(jié)構(gòu)體的總長(zhǎng)度變?yōu)?6字節(jié)。

7.3 思考:編譯器一定會(huì)按照我們指定的大小對(duì)齊嗎?

通過 aligned 屬性,我們可以顯式指定一個(gè)變量的對(duì)齊方式,那么,編譯器就一定會(huì)按照我們指定的大小對(duì)齊嗎?非也!

我們通過這個(gè)屬性聲明,其實(shí)只是建議編譯器按照這種大小地址對(duì)齊,但不能超過編譯器允許的大值。一個(gè)編譯器,對(duì)每個(gè)基本數(shù)據(jù)類型,都有默認(rèn)的大邊界對(duì)齊字節(jié)數(shù)。如果你超過了,不好意思,我不奉陪,編譯器只能按照它規(guī)定的大對(duì)齊來給你的變量分配地址。

 char c1 = 3;
 char c2 __attribute__((aligned(16))) = 4 ;
 int main(void)
 {
     printf("c1:%p\n",&c1);
     printf("c2:%p\n",&c2);
     return 0;
 }

在這個(gè)程序中,我們指定 char 型的變量 c2 以16字節(jié)對(duì)齊,然后運(yùn)行結(jié)果為:

c1:00402000
c2:00402010

我們可以看到,編譯器給 c2 分配的地址就是16字節(jié)地址對(duì)齊的,如果我們繼續(xù)修改 c2 變量按32字節(jié)對(duì)齊,你會(huì)發(fā)現(xiàn)程序的運(yùn)行結(jié)果不再會(huì)有變化,編譯器還會(huì)分配一個(gè)16字節(jié)對(duì)齊的地址,因?yàn)橐呀?jīng)超過編譯器允許的大值了。

7.4 屬性聲明:packed

aligned 屬性一般用來增大變量的地址對(duì)齊,元素之間因?yàn)榈刂穼?duì)齊會(huì)造成一定的內(nèi)存空洞。而 packed 屬性則與之相反,用來減少地址對(duì)齊,用來指定變量或類型使用最可能小的地址對(duì)齊方式。

struct data{
     char a;
     short b __attribute__((packed));
     int c __attribute__((packed));
 };
 int main(void)
 {
     struct data s;
     printf("size: %d\n",sizeof(s));
     printf("&s.a: %p\n",&s.a);
     printf("&s.b: %p\n",&s.b);
     printf("&s.c: %p\n",&s.c);
 }

在這個(gè)程序中,我們將結(jié)構(gòu)體的成員 b 和 c 使用 packed 屬性聲明,就是告訴編譯器,盡量使用最可能小的地址對(duì)齊給它們分配地址,盡可能地減少內(nèi)存空洞。程序的運(yùn)行結(jié)果如下。

 size: 7
 &s.a: 0028FF30
 &s.b: 0028FF31
 &s.c: 0028FF33

通過結(jié)果我們看到,結(jié)構(gòu)體內(nèi)各個(gè)成員地址的分配,使用最小1字節(jié)的對(duì)齊方式,導(dǎo)致整個(gè)結(jié)構(gòu)體的大小只有7個(gè)字節(jié)。

這個(gè)特性在底層驅(qū)動(dòng)開發(fā)中還是非常有用的。比如,你想定義一個(gè)結(jié)構(gòu)體,封裝一個(gè) IP 控制器的各種寄存器。在 ARM 芯片中,每一個(gè)控制器的寄存器地址空間一般是連續(xù)存在的。如果考慮數(shù)據(jù)對(duì)齊,結(jié)構(gòu)體內(nèi)有空洞,這樣就跟實(shí)際連續(xù)的寄存器地址不一致了,使用 packed 就可以避免這個(gè)問題,結(jié)構(gòu)體的每個(gè)成員都緊挨著依次分配存儲(chǔ)地址,這樣就避免了各個(gè)成員元素因地址對(duì)齊而造成的內(nèi)存空洞。

 struct data{
     char a;
     short b ;
     int c ;
 }__attribute__((packed));

我們對(duì)整個(gè)結(jié)構(gòu)體添加 packed 屬性,和分別對(duì)每個(gè)成員添加 packed 屬性,效果是一樣的。修改結(jié)構(gòu)體后,程序的運(yùn)行結(jié)果跟上面程序運(yùn)行結(jié)果相同——結(jié)構(gòu)體的大小為7,結(jié)構(gòu)體內(nèi)各成員地址相同。

7.5 Linux內(nèi)核中 aligned、packed 屬性聲明

在 Linux 內(nèi)核中,我們經(jīng)??吹?aligned 和 packed 一起使用,即對(duì)一個(gè)變量或類型同時(shí)使用 aligned 和 packed 屬性聲明。這樣做的好處是,既避免了結(jié)構(gòu)體內(nèi)因地址對(duì)齊產(chǎn)生的內(nèi)存空洞,又指定了整個(gè)結(jié)構(gòu)體的對(duì)齊方式。

 struct data{
     char a;
     short b ;
     int c ;
 }__attribute__((packed,aligned(8)));
 int main(void)
 {
     struct data s;
     printf("size: %d\n",sizeof(s));
     printf("&s.a: %p\n",&s.a);
     printf("&s.b: %p\n",&s.b);
     printf("&s.c: %p\n",&s.c);
 }

程序運(yùn)行結(jié)果如下。

 size: 8
 &s.a: 0028FF30
 &s.b: 0028FF31
 &s.c: 0028FF33

在這個(gè)程序中,結(jié)構(gòu)體 data 雖然使用 packed 屬性聲明,整個(gè)長(zhǎng)度變?yōu)?,但是我們同時(shí)又使用了 aligned(8) 指定其按8字節(jié)地址對(duì)齊,所以編譯器要在結(jié)構(gòu)體后面填充1個(gè)字節(jié),這樣整個(gè)結(jié)構(gòu)體的大小就變?yōu)?字節(jié),按8字節(jié)地址對(duì)齊。

本教程根據(jù) C語言嵌入式Linux高級(jí)編程視頻教程 第05期 改編,電子版書籍可加入QQ群:475504428 下載,更多嵌入式視頻教程,可關(guān)注:
微信公眾號(hào):宅學(xué)部落(armlinuxfun)
51CTO學(xué)院-王利濤老師:http://edu.51cto.com/sd/d344f

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

當(dāng)前標(biāo)題:嵌入式C語言自我修養(yǎng)07:地址對(duì)齊那些事兒-創(chuàng)新互聯(lián)
文章路徑:http://aaarwkj.com/article8/ccheip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)建站公司、網(wǎng)站排名、網(wǎng)站內(nèi)鏈面包屑導(dǎo)航、用戶體驗(yàn)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
天堂av免费资源在线观看| 国语av一区二区三区| 日本av二区三区在线| 日本岛国大片在线视频| 亚洲av网站女性向在线观看| 国产亚洲精品第一综合| 欧美精品久久91九色| 亚洲熟女av一区少妇| 国产一区999精品在线| 亚洲欧美日韩伦理一区| 亚洲精品一区二区三区不卡| 中文字幕在线不卡精品视频| 亚洲美女高清一区二区三区| 婷婷久久五月综合激情| 欧美女人又粗又长亚洲| 在线观看亚洲毛片网站| 欧美在线观看香蕉视频| 黄片视频免费在线观看播放| 人妻免费视频中文字幕| 欧美精品一区影片在线观看| 国产区二区三区在线视频| 日本久久精品免费网站| 日韩在线一区二区三区电影| 午夜精品视频免费91| 中文字幕不卡一区在线| 久久精品国产亚洲av麻豆网站| 五月婷婷六月丁香伊人网| 国产超大超粗超爽视频| 欧美三级特黄在线播放| 色婷婷精品一区二区三区| 国产原创av超爽剧情系列| 五月婷婷丁香视频在线| 国产一区二区传媒视频| 成人做爰片免费看视频| 免费在线观看做性小视频| 国产日韩精品综合一区| 欧美激情欧美狂野欧美精品| 91桃色网站在线免费观看| 精品久久人人做爽综合| 女人天堂网av免费看| 亚洲另类欧美日韩中文字幕|