最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

嵌入式C語(yǔ)言自我修養(yǎng)-地址對(duì)齊的核心思想

2022-05-21 20:27 作者:大方老師單片機(jī)課堂  | 我要投稿

嵌入式C語(yǔ)言自我修養(yǎng)-地址對(duì)齊的核心思想




\\\插播一條:我自己在今年年初錄制了一套還比較系統(tǒng)的入門(mén)單片機(jī)教程,想要的同學(xué)找我拿就行了免費(fèi)的(禾厶-亻言-手戈)。最近比較閑,帶做畢設(shè),帶學(xué)生參加省級(jí)以上比賽///綠色圖標(biāo)【?で】liutianwang123




7.1屬性聲明:aligned

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

inta__attribute__((aligned(8));

通過(guò) 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ì)齊方式,來(lái)給該變量分配一個(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ì)齊。

inta=1;

intb=2;

charc1=3;

charc2=4;

intmain(void)

{

printf("a: %p\n",&a);

printf("b: %p\n",&b);

printf("c1:%p\n",&c1);

printf("c2:%p\n",&c2);

return0;

}

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

a:00402000b:00402004c1:00402008c2:004這個(gè)地址不是4字節(jié)對(duì)齊的。編譯器空出3個(gè)字節(jié)單元,直接從 0x0040200C這個(gè)地址上給變量 c2分配存儲(chǔ)空間。

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

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

既然地址對(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ì)齊的地址訪問(wèn),CPU每次往內(nèi)存 RAM讀寫(xiě)數(shù)據(jù)時(shí),一個(gè)周期可以讀寫(xiě)4個(gè)字節(jié)。如果我們把一個(gè)數(shù)據(jù)放在4字節(jié)對(duì)齊的地址上,那么CPU一次就可以把數(shù)據(jù)讀寫(xiě)完畢;如果我們把一個(gè) int型數(shù)據(jù)放在一個(gè)非4字節(jié)對(duì)齊的地址上,那 CPU就要分2次才能把這個(gè)4字節(jié)大小的數(shù)據(jù)讀寫(xiě)完畢。

為了配合計(jì)算機(jī)的硬件設(shè)計(jì),編譯器在編譯程序時(shí),對(duì)于一些基本數(shù)據(jù)類型,比如 int、char、short、float等,會(huì)按照其數(shù)據(jù)類型的大小進(jìn)行地址對(duì)齊,按照這種地址對(duì)齊方式分配的存儲(chǔ)地址,CPU一次就可以讀寫(xiě)完畢。雖然邊界對(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)體的末尾填充一些空間。

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

structdata{

chara;

intb;

shortc;

}

intmain(void)

{

structdatas;

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ì)齊,或者說(shuō)結(jié)構(gòu)體的整體長(zhǎng)度要為其最大成員字節(jié)數(shù)的整數(shù)倍,如果不是整數(shù)倍要補(bǔ)齊。因?yàn)榻Y(jié)構(gòu)體最大成員 int為4個(gè)字節(jié),或者說(shuō)按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)度不一樣,我們修改一下上面的程序。

structdata{

chara;

shortb;

intc;

};

intmain(void)

{

structdatas;

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ì)齊:

structdata{

chara;

shortb__attribute__((aligned(4)));

intc;

};

程序運(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ì)齊方式。

structdata{

chara;

shortb;

intc;

}__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é)。通過(guò)前面學(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ì)齊嗎?

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

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

charc1=3;

charc2__attribute__((aligned(16)))=4;

intmain(void)

{

printf("c1:%p\n",&c1);

printf("c2:%p\n",&c2);

return0;

}

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

c1:00402000c2: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)超過(guò)編譯器允許的最大值了。


7.4屬性聲明:packed

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

structdata{

chara;

shortb__attribute__((packed));

intc__attribute__((packed));

};

intmain(void)

{

structdatas;

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

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

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

structdata{

chara;

shortb;

intc;

}__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ì)齊方式。

structdata{

chara;

shortb;

intc;

}__attribute__((packed,aligned(8)));

intmain(void)

{

structdatas;

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ì)齊。

嵌入式C語(yǔ)言自我修養(yǎng)-地址對(duì)齊的核心思想的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
宁城县| 商洛市| 女性| 广丰县| 邵阳县| 利川市| 珠海市| 崇阳县| 五河县| 南皮县| 丹凤县| 丹寨县| 马鞍山市| 于都县| 临海市| 辛集市| 昆明市| 琼海市| 青冈县| 永寿县| 恩施市| 清水县| 巴东县| 新龙县| 上饶市| 县级市| 绍兴市| 浮山县| 乐山市| 沭阳县| 开远市| 讷河市| 四会市| 油尖旺区| 颍上县| 汉源县| 肃宁县| 外汇| 蒙城县| 炉霍县| 中方县|