本文提供了有關(guān)嵌入式C編程中的結(jié)構(gòu)的一些基本信息。
在介紹了結(jié)構(gòu)之后,我們將看一下這個強(qiáng)大的數(shù)據(jù)對象的一些重要應(yīng)用。然后,我們將檢查C語言語法以聲明結(jié)構(gòu)。最后,我們將簡要介紹數(shù)據(jù)對齊要求。我們將看到,通過簡單地重新排列其成員的順序,我們也許可以減小結(jié)構(gòu)的大小。
結(jié)構(gòu)體
邏輯上彼此相關(guān)的多個相同類型的變量可以分組為一個數(shù)組。在組上而不是自變量的集合上工作使我們可以整理數(shù)據(jù)并更方便地使用它。例如,我們可以定義以下數(shù)組來存儲將語音輸入數(shù)字化的ADC的最后50個樣本:
uint16_tvoice[50];
請注意,uint16_t是寬度為16位的無符號整數(shù)類型。這在C標(biāo)準(zhǔn)庫stdint.h中定義,該庫提供與系統(tǒng)規(guī)格無關(guān)的特定位長的數(shù)據(jù)類型。
數(shù)組可用于對同一數(shù)據(jù)類型的多個變量進(jìn)行分組。如果不同數(shù)據(jù)類型的變量之間存在聯(lián)系怎么辦?我們可以在程序中將這些變量視為一組嗎?例如,假設(shè)我們需要指定 上面生成語音陣列的ADC的采樣率。我們可以定義一個float變量來存儲采樣率:
floatsample_rate;
盡管變量voice和sample_rate彼此相關(guān),但它們被定義為兩個獨立變量。為了使這兩個變量相互關(guān)聯(lián),我們可以使用稱為結(jié)構(gòu)的C語言強(qiáng)大的數(shù)據(jù)構(gòu)造。結(jié)構(gòu)允許我們將不同的數(shù)據(jù)類型分組,并將它們作為單個數(shù)據(jù)對象處理。一個結(jié)構(gòu)可以包括不同種類的變量類型,例如其他結(jié)構(gòu),指向函數(shù)的指針,指向結(jié)構(gòu)的指針等。對于語音示例,我們可以使用以下結(jié)構(gòu):
structrecord{ uint16_tvoice[50]; floatsample_rate; };
在這種情況下,我們有一個稱為record的結(jié)構(gòu),該結(jié)構(gòu) 具有兩個不同的成員或字段:第一個成員是uint16_t元素的數(shù)組,第二個成員是float類型的變量。語法以關(guān)鍵字struct開頭。struct關(guān)鍵字后的單詞是一個可選名稱,用于以后引用該結(jié)構(gòu)。我們將在本文的其余部分中討論定義和使用結(jié)構(gòu)的其他細(xì)節(jié)。
為什么結(jié)構(gòu)很重要?
上面的示例指出了結(jié)構(gòu)的重要應(yīng)用,即定義了可以將不同類型的各個變量相互關(guān)聯(lián)的依賴于應(yīng)用的數(shù)據(jù)對象。這不僅導(dǎo)致處理數(shù)據(jù)的有效方式,而且使我們能夠?qū)崿F(xiàn)稱為數(shù)據(jù)結(jié)構(gòu)的專門結(jié)構(gòu)。
數(shù)據(jù)結(jié)構(gòu)可用于各種應(yīng)用程序,例如兩個嵌入式系統(tǒng)之間的消息傳遞以及將從傳感器收集的數(shù)據(jù)存儲在不連續(xù)的內(nèi)存位置中。
圖1.結(jié)構(gòu)可用于實現(xiàn)鏈表。
此外,當(dāng)程序需要訪問內(nèi)存映射的微控制器外圍設(shè)備的寄存器時,結(jié)構(gòu)是有用的數(shù)據(jù)對象。在下一篇文章中,我們將介紹結(jié)構(gòu)應(yīng)用程序。
圖2.STM32MCU的存儲器映射。圖片由帶ARM的嵌入式系統(tǒng)提供。
聲明結(jié)構(gòu)
要使用結(jié)構(gòu),我們首先需要指定一個結(jié)構(gòu)模板。考慮下面的示例代碼:
structrecord{ uint16_tvoice[4]; floatsample_rate; };
這指定了用于創(chuàng)建此類型的將來變量的布局或模板。該模板包括一個uint16_t數(shù)組和一個float類型的變量。模板的名稱為record,它位于關(guān)鍵字struct之后。值得一提的是,沒有用于存儲結(jié)構(gòu)模板的內(nèi)存分配。僅在定義了基于此布局的結(jié)構(gòu)變量之后,才進(jìn)行內(nèi)存分配。以下代碼聲明 了上述模板的mic1變量:
structrecordmic1;
現(xiàn)在,為變量mic1分配了一部分內(nèi)存。它有空間存儲數(shù)組的四個uint16_t元素和一個float變量。
可以使用成員運算符(。)訪問結(jié)構(gòu)的成員。例如,以下代碼將100分配給數(shù)組的第一個元素,并將sample_rate的值復(fù)制到fs變量(該變量必須是float類型)。
mic1.voice[0]=100; fs=mic1.sample_rate;
聲明結(jié)構(gòu)的其他方法
在上一節(jié)中,我們介紹了一種聲明結(jié)構(gòu)的方法。C語言支持其他一些格式,本節(jié)將進(jìn)行介紹。在整個程序中,您可能會堅持使用一種格式,但有時可能會對其他格式有所幫助。
聲明結(jié)構(gòu)模板的一般語法為:
structtag_name{ type_1member_1; type_2member_2; … type_nmember_n; }variable_name;
該TAG_NAME和變量名是可選的標(biāo)識符。通常,我們會至少看到這兩個標(biāo)識符之一,但是在某些情況下,我們可以消除這兩個標(biāo)識符。
語法1:當(dāng)同時存在tag_name和variable_name時,我們在模板后面定義結(jié)構(gòu)變量。使用此語法,我們可以重寫以下示例:
structrecord{ uint16_tvoice[4]; floatsample_rate; }mic1;
現(xiàn)在,如果我們需要定義另一個變量(mic2),我們可以編寫
structrecordmic2;
語法2:僅 包含variable_name。使用此語法,我們可以按以下方式重寫上一節(jié)中的示例:
struct{ uint16_tvoice[4]; floatsample_rate; }mic1;
在這種情況下,我們必須在模板之后定義所有變量,而我們以后不能在程序中定義任何其他變量(因為模板沒有名稱,以后也不能引用它)。
語法3:在這種情況下,沒有tag_name或variable_name。以這種方式定義的結(jié)構(gòu)模板稱為匿名結(jié)構(gòu)。可以在另一個結(jié)構(gòu)或聯(lián)合中定義匿名結(jié)構(gòu)。下面是一個示例:
structtest{ //Anonymousstructure struct{ floatf; chara; }; }test_var;
要訪問上述匿名結(jié)構(gòu)的成員,我們可以使用成員運算符(。)。以下代碼將1.2分配給成員f。
test_var.f=1.2;
由于該結(jié)構(gòu)是匿名的,因此我們僅使用一次成員運算符訪問其成員。如果它的名稱如下面的示例所示,我們將不得不兩次使用成員運算符:
structtest{ struct{ floatf; chara; }nested; }test_var;
在這種情況下,我們應(yīng)該使用以下代碼將1.2分配給f:
test_var.nested.f=1.2;
如您所見,匿名結(jié)構(gòu)可以使代碼更具可讀性,而又不那么冗長。也可以將typedef關(guān)鍵字與結(jié)構(gòu)一起使用以定義新的數(shù)據(jù)類型。我們將在以后的文章中介紹這種方法。
結(jié)構(gòu)的內(nèi)存布局
C標(biāo)準(zhǔn)保證結(jié)構(gòu)的成員將按照在結(jié)構(gòu)中聲明成員的順序一個接一個地位于內(nèi)存中。第一個成員的內(nèi)存地址將與結(jié)構(gòu)本身的地址相同。考慮以下示例:
將分配四個存儲位置來存儲變量c,d,e和f。內(nèi)存位置的順序?qū)⑴c聲明成員的順序匹配:c的位置將具有最低的地址,然后是d,e,最后出現(xiàn)f。我們需要多少字節(jié)來存儲此結(jié)構(gòu)?考慮到變量的大小,我們知道至少需要1 + 4 + 1 + 2 = 8個字節(jié)來存儲此結(jié)構(gòu)。但是,如果我們將此代碼編譯為32位計算機(jī),則會令人驚訝地觀察到MyStruct的大小是12個字節(jié)而不是8個字節(jié)!這是由于以下事實:編譯器在為結(jié)構(gòu)的不同成員分配內(nèi)存時具有某些約束。例如,一個32位整數(shù)只能存儲在其地址可被4整除的內(nèi)存位置。實施這種約束,稱為數(shù)據(jù)對齊要求,以使處理器更有效地訪問變量。數(shù)據(jù)對齊會導(dǎo)致內(nèi)存布局浪費一些空間(或填充)。僅在這里介紹該主題。我們將在本系列的下一篇文章中詳細(xì)介紹。
圖3.數(shù)據(jù)對齊會導(dǎo)致內(nèi)存布局中的空間浪費(或填充)。
意識到數(shù)據(jù)對齊要求后,我們也許可以重新排列結(jié)構(gòu)中成員的順序,并使內(nèi)存使用效率更高。例如,如果我們按如下所示重寫上述結(jié)構(gòu),則在32位計算機(jī)上,其大小將減小為8個字節(jié)。
structTest2{ uint32_td; uint16_tf; uint8_tc; uint8_te; }MyStruct;
對于受內(nèi)存限制的嵌入式系統(tǒng),將數(shù)據(jù)對象的大小從12個字節(jié)減少到8個字節(jié)可節(jié)省大量資金,尤其是當(dāng)程序需要許多此類數(shù)據(jù)對象時。
下一篇文章將更詳細(xì)地討論數(shù)據(jù)對齊,并研究在嵌入式系統(tǒng)中使用結(jié)構(gòu)的一些示例。
概要
結(jié)構(gòu)允許我們定義依賴于應(yīng)用程序的數(shù)據(jù)對象,這些對象可以將不同類型的各個變量相互關(guān)聯(lián)。這導(dǎo)致了一種有效的數(shù)據(jù)處理方法。
稱為數(shù)據(jù)結(jié)構(gòu)的專用結(jié)構(gòu)可用于各種應(yīng)用程序,例如兩個嵌入式系統(tǒng)之間的消息傳遞以及將從傳感器收集的數(shù)據(jù)存儲在不連續(xù)的內(nèi)存位置中。
當(dāng)我們需要訪問存儲器映射的微控制器外設(shè)的寄存器時,結(jié)構(gòu)很有用。
通過重新排列結(jié)構(gòu)中成員的順序,我們也許可以使內(nèi)存使用效率更高。
-
C語言
+關(guān)注
關(guān)注
180文章
7605瀏覽量
136934 -
結(jié)構(gòu)體數(shù)據(jù)
+關(guān)注
關(guān)注
0文章
3瀏覽量
5958
發(fā)布評論請先 登錄
相關(guān)推薦
評論