精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

嵌入式 C 語(yǔ)言中三塊難啃的硬骨頭

開發(fā) 后端
C語(yǔ)言在嵌入式學(xué)習(xí)中是必備的知識(shí),審核大部分操作都要圍繞C語(yǔ)言進(jìn)行,而其中有三塊“難啃的硬骨頭”幾乎是公認(rèn)級(jí)別的。

 C語(yǔ)言在嵌入式學(xué)習(xí)中是必備的知識(shí),審核大部分操作都要圍繞C語(yǔ)言進(jìn)行,而其中有三塊“難啃的硬骨頭”幾乎是公認(rèn)級(jí)別的。

01. 指針

指針公認(rèn)最難理解的概念,也是讓很多初學(xué)者選擇放棄的直接原因

指針之所以難理解,因?yàn)橹羔槺旧砭褪且粋€(gè)變量,是一個(gè)非常特殊的變量,專門存放地址的變量,這個(gè)地址需要給申請(qǐng)空間才能裝東西,而且因?yàn)槭莻€(gè)變量可以中間賦值,這么一倒騰很多人就開始犯暈了,繞不開彎了。C語(yǔ)言之所以被很多高手所喜歡,就是指針的魅力,中間可以靈活的切換,執(zhí)行效率超高,這點(diǎn)也是讓小白暈菜的地方。

指針是學(xué)習(xí)繞不過去的知識(shí)點(diǎn),而且學(xué)完C語(yǔ)言,下一步緊接著切換到數(shù)據(jù)結(jié)構(gòu)和算法,指針是切換的重點(diǎn),指針搞不定下一步進(jìn)行起來(lái)就很難,會(huì)讓很多人放棄繼續(xù)學(xué)習(xí)的勇氣。

指針直接對(duì)接內(nèi)存結(jié)構(gòu),常見的C語(yǔ)言里面的指針亂指,數(shù)組越界根本原因就是內(nèi)存問題。在指針這個(gè)點(diǎn)有無(wú)窮無(wú)盡的發(fā)揮空間。很多編程的技巧都在此集結(jié)。

指針還涉及如何申請(qǐng)釋放內(nèi)存,如果釋放不及時(shí)就會(huì)出現(xiàn)內(nèi)存泄露的情況,指針是高效好用,但不徹底搞明白對(duì)于有些人來(lái)說簡(jiǎn)直就是噩夢(mèng)。

在概念方面問題可以參見此前推文《對(duì)于C語(yǔ)言指針最詳盡的講解》,那么在指針方面可以參見一下大神的經(jīng)驗(yàn):

▎復(fù)雜類型說明

要了解指針,多多少少會(huì)出現(xiàn)一些比較復(fù)雜的類型。所以先介紹一下如何完全理解一個(gè)復(fù)雜類型。

要理解復(fù)雜類型其實(shí)很簡(jiǎn)單,一個(gè)類型里會(huì)出現(xiàn)很多運(yùn)算符,他們也像普通的表達(dá)式一樣,有優(yōu)先級(jí),其優(yōu)先級(jí)和運(yùn)算優(yōu)先級(jí)一樣。

所以筆者總結(jié)了一下其原則:從變量名處起,根據(jù)運(yùn)算符優(yōu)先級(jí)結(jié)合,一步一步分析。

下面讓我們先從簡(jiǎn)單的類型開始慢慢分析吧。

  •  int p;

這是一個(gè)普通的整型變量

  •  int p;

首先從P處開始,先與結(jié)合,所以說明P是一個(gè)指針。然后再與int結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型為int型,所以P是一個(gè)返回整型數(shù)據(jù)的指針

  •  int p[3];

首先從P處開始,先與[]結(jié)合,說明P是一個(gè)數(shù)組。然后與int結(jié)合,說明數(shù)組里的元素是整型的,所以P是一個(gè)由整型數(shù)據(jù)組成的數(shù)組。

  •  int *p[3];

首先從P處開始,先與[]結(jié)合,因?yàn)槠鋬?yōu)先級(jí)比高,所以P是一個(gè)數(shù)組。然后再與結(jié)合,說明數(shù)組里的元素是指針類型。之后再與int結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型是整型的,所以P是一個(gè)由返回整型數(shù)據(jù)的指針?biāo)M成的數(shù)組。

  •  int (*p)[3];

首先從P處開始,先與結(jié)合,說明P是一個(gè)指針。然后再與[]結(jié)合(與"()"這步可以忽略,只是為了改變優(yōu)先級(jí)),說明指針?biāo)赶虻膬?nèi)容是一個(gè)數(shù)組。之后再與int結(jié)合,說明數(shù)組里的元素是整型的。所以P是一個(gè)指向由整型數(shù)據(jù)組成3個(gè)整數(shù)的指針。

  •  int **p;

首先從P開始,先與*結(jié)合,說明P是一個(gè)指針。然后再與*結(jié)合,說明指針?biāo)赶虻脑厥侵羔槨V笤倥cint結(jié)合,說明該指針?biāo)赶虻脑厥钦蛿?shù)據(jù)。由于二級(jí)指針以及更高級(jí)的指針極少用在復(fù)雜的類型中,所以后面更復(fù)雜的類型我們就不考慮多級(jí)指針了,最多只考慮一級(jí)指針。

  •  int p(int);

從P處起,先與()結(jié)合,說明P是一個(gè)函數(shù)。然后進(jìn)入()里分析,說明該函數(shù)有一個(gè)整型變量的參數(shù),之后再與外面的int結(jié)合,說明函數(shù)的返回值是一個(gè)整型數(shù)據(jù)。

  •  Int (*p)(int);

從P處開始,先與指針結(jié)合,說明P是一個(gè)指針。然后與()結(jié)合,說明指針指向的是一個(gè)函數(shù)。之后再與()里的int結(jié)合,說明函數(shù)有一個(gè)int型的參數(shù),再與最外層的int結(jié)合,說明函數(shù)的返回類型是整型,所以P是一個(gè)指向有一個(gè)整型參數(shù)且返回類型為整型的函數(shù)的指針。

  •   int (p(int))[3];

可以先跳過,不看這個(gè)類型,過于復(fù)雜。從P開始,先與()結(jié)合,說明P是一個(gè)函數(shù)。然后進(jìn)入()里面,與int結(jié)合,說明函數(shù)有一個(gè)整型變量參數(shù)。然后再與外面的結(jié)合,說明函數(shù)返回的是一個(gè)指針。之后到最外面一層,先與[]結(jié)合,說明返回的指針指向的是一個(gè)數(shù)組。接著再與結(jié)合,說明數(shù)組里的元素是指針,最后再與int結(jié)合,說明指針指向的內(nèi)容是整型數(shù)據(jù)。所以P是一個(gè)參數(shù)為一個(gè)整數(shù)據(jù)且返回一個(gè)指向由整型指針變量組成的數(shù)組的指針變量的函數(shù)。

說到這里也就差不多了。理解了這幾個(gè)類型,其它的類型對(duì)我們來(lái)說也是小菜了。不過一般不會(huì)用太復(fù)雜的類型,那樣會(huì)大大減小程序的可讀性,請(qǐng)慎用。這上面的幾種類型已經(jīng)足夠我們用了。

▎細(xì)說指針

指針是一個(gè)特殊的變量,它里面存儲(chǔ)的數(shù)值被解釋成為內(nèi)存里的一個(gè)地址。

要搞清一個(gè)指針需要搞清指針的四方面的內(nèi)容:指針的類型、指針?biāo)赶虻念愋汀⒅羔樀闹祷蛘呓兄羔標(biāo)赶虻膬?nèi)存區(qū)、指針本身所占據(jù)的內(nèi)存區(qū)。讓我們分別說明。

先聲明幾個(gè)指針放著做例子:

(1)int*ptr;

(2)char*ptr;

(3)int**ptr;

(4)int(*ptr)[3];

(5)int*(*ptr)[4];

▎指針的類型

從語(yǔ)法的角度看,小伙伴們只要把指針聲明語(yǔ)句里的指針名字去掉,剩下的部分就是這個(gè)指針的類型。這是指針本身所具有的類型。

讓我們看看上述例子中各個(gè)指針的類型:

(1)intptr;//指針的類型是int

(2)charptr;//指針的類型是char

(3)intptr;//指針的類型是int

(4)int(ptr)[3];//指針的類型是int()[3]

(5)int*(ptr)[4];//指針的類型是int(*)[4]

怎么樣?找出指針的類型的方法是不是很簡(jiǎn)單?

▎指針?biāo)赶虻念愋?/h3>

當(dāng)通過指針來(lái)訪問指針?biāo)赶虻膬?nèi)存區(qū)時(shí),指針?biāo)赶虻念愋蜎Q定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來(lái)看待。

從語(yǔ)法上看,小伙伴們只需把指針聲明語(yǔ)句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針?biāo)赶虻念愋汀?/p>

上述例子中各個(gè)指針?biāo)赶虻念愋停?/p>

(1)intptr; //指針?biāo)赶虻念愋褪莍nt

(2)char*ptr; //指針?biāo)赶虻牡念愋褪莄har*

(3)int*ptr; //指針?biāo)赶虻牡念愋褪莍nt*

(4)int(*ptr)[3]; //指針?biāo)赶虻牡念愋褪莍nt(*)[3]

(5)int*(*ptr)[4]; //指針?biāo)赶虻牡念愋褪莍nt*(*)[4]

在指針的算術(shù)運(yùn)算中,指針?biāo)赶虻念愋陀泻艽蟮淖饔谩?/p>

指針的類型(即指針本身的類型)和指針?biāo)赶虻念愋褪莾蓚€(gè)概念。當(dāng)小伙伴們對(duì)C 越來(lái)越熟悉時(shí),就會(huì)發(fā)現(xiàn),把與指針攪和在一起的"類型"這個(gè)概念分成"指針的類型"和"指針?biāo)赶虻念愋?quot;兩個(gè)概念,是精通指針的關(guān)鍵點(diǎn)之一。

筆者看了不少書,發(fā)現(xiàn)有些寫得差的書中,就把指針的這兩個(gè)概念攪在一起了,所以看起書來(lái)前后矛盾,越看越糊涂。

▎指針的值

即指針?biāo)赶虻膬?nèi)存區(qū)或地址。

指針的值是指針本身存儲(chǔ)的數(shù)值,這個(gè)值將被編譯器當(dāng)作一個(gè)地址,而不是一個(gè)一般的數(shù)值。

在32位程序里,所有類型的指針的值都是一個(gè)32位整數(shù),因?yàn)?2位程序里內(nèi)存地址全都是32位長(zhǎng)。指針?biāo)赶虻膬?nèi)存區(qū)就是從指針的值所代表的那個(gè)內(nèi)存地址開始,長(zhǎng)度為si zeof(指針?biāo)赶虻念愋?的一片內(nèi)存區(qū)。

以后,我們說一個(gè)指針的值是XX,就相當(dāng)于說該指針指向了以XX為首地址的一片內(nèi)存區(qū)域;我們說一個(gè)指針指向了某塊內(nèi)存區(qū)域,就相當(dāng)于說該指針的值是這塊內(nèi)存區(qū)域的首地址。

指針?biāo)赶虻膬?nèi)存區(qū)和指針?biāo)赶虻念愋褪莾蓚€(gè)完全不同的概念。在例一中,指針?biāo)赶虻念愋鸵呀?jīng)有了,但由于指針還未初始化,所以它所指向的內(nèi)存區(qū)是不存在的,或者說是無(wú)意義的。

以后,每遇到一個(gè)指針,都應(yīng)該問問:這個(gè)指針的類型是什么?指針指的類型是什么?該指針指向了哪里?

▎指針本身所占據(jù)的內(nèi)存區(qū)

指針本身占了多大的內(nèi)存?只要用函數(shù)sizeof(指針的類型)測(cè)一下就知道了。在32位平臺(tái)里,指針本身占據(jù)4個(gè)字節(jié)的長(zhǎng)度。指針本身占據(jù)的內(nèi)存這個(gè)概念在判斷一個(gè)指針表達(dá)式是否是左值時(shí)很有用。

02. 函數(shù)概念

面向過程對(duì)象模塊的基本單位,以及對(duì)應(yīng)各種組合,函數(shù)指針,指針函數(shù)

一個(gè)函數(shù)就是一個(gè)業(yè)務(wù)邏輯塊,是面向過程,單元模塊的最小單元,而且在函數(shù)的執(zhí)行過程中,形參,實(shí)參如何交換數(shù)據(jù),如何將數(shù)據(jù)傳遞出去,如何設(shè)計(jì)一個(gè)合理的函數(shù),不單單是解決一個(gè)功能,還要看是不是能夠復(fù)用,避免重復(fù)造輪子。

函數(shù)指針和指針函數(shù),表面是兩個(gè)字面意思的互換實(shí)際上含義截然不同,指針函數(shù)比較好理解,就是返回指針的一個(gè)函數(shù),函數(shù)指針這個(gè)主要用在回調(diào)函數(shù),很多人覺得函數(shù)都沒還搞明白,回調(diào)函數(shù)更暈菜了。其實(shí)可以通俗的理解指向函數(shù)的指針,本身是一個(gè)指針變量,只不過在初始化的時(shí)候指向了函數(shù),這又回到了指針層面。沒搞明白指針再次深入的向前走特別難。

C語(yǔ)言的開發(fā)者們?yōu)楹髞?lái)的開發(fā)者做了一些省力氣的事情,他們編寫了大量代碼,將常見的基本功能都完成了,可以讓別人直接拿來(lái)使用。但是那么多代碼,如何從中找到自己需要的呢?將所有代碼都拿來(lái)顯然是不太現(xiàn)實(shí)。

但是這些代碼,早已被早期的開發(fā)者們分門別類地放在了不同的文件中,并且每一段代碼都有唯一的名字。所以其實(shí)學(xué)習(xí)C語(yǔ)言并沒有那么難,尤其是可以在動(dòng)手鍛煉做項(xiàng)目中進(jìn)行。使用代碼時(shí),只要在對(duì)應(yīng)的名字后面加上( )就可以。這樣的一段代碼就是函數(shù),函數(shù)能夠獨(dú)立地完成某個(gè)功能,一次編寫完成后可以多次使用。

很多初學(xué)者可能都會(huì)把C語(yǔ)言中的函數(shù)和數(shù)學(xué)中的函數(shù)概念搞混淆。其實(shí)真相并沒有那么復(fù)雜,C語(yǔ)言中的函數(shù)是有規(guī)律可循跡的,只要搞清楚了概念你會(huì)發(fā)現(xiàn)還挺有意思的。

函數(shù)的英文名稱是 Function,對(duì)應(yīng)翻譯過來(lái)的中文還有“功能”的意思。C語(yǔ)言中的函數(shù)也跟功能有著密切的關(guān)系。

我們來(lái)看一小段C語(yǔ)言代碼: 

  1. #include<stdio.h>  
  2. int main()  
  3.  
  4. puts("Hello World");  
  5. return 0;  

把目光放在第4行代碼上,這行代碼會(huì)在顯示器上輸出“Hello World”。前面我們已經(jīng)講過,puts 后面要帶( ),字符串也要放在( )中。

在C語(yǔ)言中,有的語(yǔ)句使用時(shí)不能帶括號(hào),有的語(yǔ)句必須帶括號(hào)。帶括號(hào)的就是函數(shù)(Function)。

C語(yǔ)言提供了很多功能,我們只需要一句簡(jiǎn)單的代碼就能夠使用。但是這些功能的底層都比較復(fù)雜,通常是軟件和硬件的結(jié)合,還要要考慮很多細(xì)節(jié)和邊界,如果將這些功能都交給程序員去完成,那將極大增加程序員的學(xué)習(xí)成本,降低編程效率。

有了函數(shù)之后,C語(yǔ)言的編程效率就好像有了神器一樣,開發(fā)者們只需要隨時(shí)調(diào)用就可以了,像進(jìn)程函數(shù)、操作函數(shù)、時(shí)間日期函數(shù)等都可以幫助我們直接實(shí)現(xiàn)C語(yǔ)言本身的功能。

C語(yǔ)言函數(shù)是可以重復(fù)使用的。

函數(shù)的一個(gè)明顯特征就是使用時(shí)必須帶括號(hào)( ),必要的話,括號(hào)中還可以包含待處理的數(shù)據(jù)。例如puts("尚觀科技")就使用了一段具有輸出功能的代碼,這段代碼的名字是 puts,"尚觀科技" 是要交給這段代碼處理的數(shù)據(jù)。使用函數(shù)在編程中有專業(yè)的稱呼,叫做函數(shù)調(diào)用(Function Call)。

如果函數(shù)需要處理多個(gè)數(shù)據(jù),那么它們之間使用逗號(hào),分隔,例如:

pow(10, 2);

該函數(shù)用來(lái)求10的2次方。

好了,看到這里你有沒有覺得其實(shí)C語(yǔ)言函數(shù)還是比較有意思的,而且并沒有那么復(fù)雜困難。以后再遇到菜鳥小白的時(shí)候,你一口一個(gè)C語(yǔ)言的函數(shù),說不定就能當(dāng)場(chǎng)引來(lái)無(wú)數(shù)膜拜的目光。

03. 結(jié)構(gòu)體,遞歸

很多在大學(xué)學(xué)習(xí)C語(yǔ)言的,很多課程都沒學(xué)完,結(jié)構(gòu)體都沒學(xué)到,因?yàn)閺恼鹿?jié)的安排來(lái)看好像,結(jié)構(gòu)體學(xué)習(xí)放在教材的后半部分了,弄得很多學(xué)生覺得結(jié)構(gòu)體不重要,如果只是應(yīng)付學(xué)校的考試,或者就是為了混個(gè)畢業(yè)證,的確學(xué)的意義不大。

如果想從事編程這個(gè)行業(yè),對(duì)這個(gè)概念還不了解,基本上無(wú)法構(gòu)造數(shù)據(jù)模型,沒有一個(gè)業(yè)務(wù)體是完全使用原生數(shù)據(jù)類型來(lái)完成的,很多高手在設(shè)計(jì)數(shù)據(jù)模型的時(shí)候,一般先把頭文件中的結(jié)構(gòu)體數(shù)據(jù)整理出來(lái)。然后設(shè)計(jì)好功能函數(shù)的參數(shù),以及名字,然后才真正開始寫c源碼。

如果從節(jié)省空間考慮結(jié)構(gòu)體里面的數(shù)據(jù)放的順序不一樣在內(nèi)存中占用的空間也不一樣,結(jié)構(gòu)體與結(jié)構(gòu)體之間賦值,結(jié)構(gòu)體存在指針那么賦值要特別注意,需要進(jìn)行深度的賦值。

遞歸一般用于從頭到位統(tǒng)計(jì)或者羅列一些數(shù)據(jù),在使用的時(shí)候很多初學(xué)者都覺得別扭,怎么還能自己調(diào)用自己?而且在使用的時(shí)候,一定設(shè)置好跳出的條件,不然無(wú)休止的進(jìn)行下去,真就成無(wú)線死循環(huán)了。

對(duì)于結(jié)構(gòu)體方面的知識(shí),可以參見此前推送的文章《C語(yǔ)言結(jié)構(gòu)體(struct)最全的講解(萬(wàn)字干貨)》。具體也可以參見大佬的經(jīng)驗(yàn):

相信大家對(duì)于結(jié)構(gòu)體都不陌生。在此,分享出本人對(duì)C語(yǔ)言結(jié)構(gòu)體的研究和學(xué)習(xí)的總結(jié)。如果你發(fā)現(xiàn)這個(gè)總結(jié)中有你以前所未掌握的,那本文也算是有點(diǎn)價(jià)值了。當(dāng)然,水平有限,若發(fā)現(xiàn)不足之處懇請(qǐng)指出。代碼文件test.c我放在下面。

在此,我會(huì)圍繞以下2個(gè)問題來(lái)分析和應(yīng)用C語(yǔ)言結(jié)構(gòu)體:

1. C語(yǔ)言中的結(jié)構(gòu)體有何作用

2. 結(jié)構(gòu)體成員變量?jī)?nèi)存對(duì)齊有何講究(重點(diǎn))

對(duì)于一些概念的說明,我就不把C語(yǔ)言教材上的定義搬上來(lái)。我們坐下來(lái)慢慢聊吧。

1. 結(jié)構(gòu)體有何作用

三個(gè)月前,教研室里一個(gè)學(xué)長(zhǎng)在華為南京研究院的面試中就遇到這個(gè)問題。當(dāng)然,這只是面試中最基礎(chǔ)的問題。如果問你你怎么回答?

我的理解是這樣的,C語(yǔ)言中結(jié)構(gòu)體至少有以下三個(gè)作用:

(1) 有機(jī)地組織了對(duì)象的屬性。

比如,在STM32的RTC開發(fā)中,我們需要數(shù)據(jù)來(lái)表示日期和時(shí)間,這些數(shù)據(jù)通常是年、月、日、時(shí)、分、秒。如果我們不用結(jié)構(gòu)體,那么就需要定義6個(gè)變量來(lái)表示。這樣的話程序的數(shù)據(jù)結(jié)構(gòu)是松散的,我們的數(shù)據(jù)結(jié)構(gòu)最好是“高內(nèi)聚,低耦合”的。所以,用一個(gè)結(jié)構(gòu)體來(lái)表示更好,無(wú)論是從程序的可讀性還是可移植性還是可維護(hù)性皆是:

typedef struct //公歷日期和時(shí)間結(jié)構(gòu)體 

  1.  
  2. vu16 year;  
  3. vu8 month;  
  4. vu8 date;  
  5. vu8 hour;  
  6. vu8 min;  
  7. vu8 sec;  
  8. }_calendar_obj;  
  9. _calendar_obj calendar; //定義結(jié)構(gòu)體變量 

(2) 以修改結(jié)構(gòu)體成員變量的方法代替了函數(shù)(入口參數(shù))的重新定義。

如果說結(jié)構(gòu)體有機(jī)地組織了對(duì)象的屬性表示結(jié)構(gòu)體“中看”,那么以修改結(jié)構(gòu)體成員變量的方法代替函數(shù)(入口參數(shù))的重新定義就表示了結(jié)構(gòu)體“中用”。繼續(xù)以上面的結(jié)構(gòu)體為例子,我們來(lái)分析。假如現(xiàn)在我有如下函數(shù)來(lái)顯示日期和時(shí)間: 

  1. void DsipDateTime( _calendar_obj DateTimeVal) 

那么我們只要將一個(gè)_calendar_obj這個(gè)結(jié)構(gòu)體類型的變量作為實(shí)參調(diào)用DsipDateTime()即可,DsipDateTime()通過DateTimeVal的成變量來(lái)實(shí)現(xiàn)內(nèi)容的顯示。如果不用結(jié)構(gòu)體,我們很可能需要寫這樣的一個(gè)函數(shù): 

  1. void DsipDateTime( vu16 year,vu8 month,vu8 date,vu8 hour,vu8 min,vu8 sec) 

顯然這樣的形參很不可觀,數(shù)據(jù)結(jié)構(gòu)管理起來(lái)也很繁瑣。如果某個(gè)函數(shù)的返回值得是一個(gè)表示日期和時(shí)間的數(shù)據(jù),那就更復(fù)雜了。這只是一方面。

另一方面,如果用戶需要表示日期和時(shí)間的數(shù)據(jù)中還要包含星期(周),這個(gè)時(shí)候,如果之前沒有用機(jī)構(gòu)體,那么應(yīng)該在DsipDateTime()函數(shù)中在增加一個(gè)形參vu8 week: 

  1. void DsipDateTime( vu16 year,vu8 month,vu8 date,vu8 week,vu8 hour,vu8 min,vu8 sec) 

可見這種方法來(lái)傳遞參數(shù)非常繁瑣。所以以結(jié)構(gòu)體作為函數(shù)的入口參數(shù)的好處之一就是函數(shù)的聲明void DsipDateTime( _calendar_obj DateTimeVal)不需要改變,只需要增加結(jié)構(gòu)體的成員變量,然后在函數(shù)的內(nèi)部實(shí)現(xiàn)上對(duì)calendar.week作相應(yīng)的處理即可。這樣,在程序的修改、維護(hù)方面作用顯著。 

  1. typedef struct //公歷日期和時(shí)間結(jié)構(gòu)體  
  2.  
  3. vu16 year;  
  4. vu8 month;  
  5. vu8 date;  
  6. vu8 week;  
  7. vu8 hour;  
  8. vu8 min;  
  9. vu8 sec;  
  10. }_calendar_obj;  
  11. _calendar_obj calendar; //定義結(jié)構(gòu)體變量 

(3) 結(jié)構(gòu)體的內(nèi)存對(duì)齊原則可以提高CPU對(duì)內(nèi)存的訪問速度(以空間換取時(shí)間)。

并且,結(jié)構(gòu)體成員變量的地址可以根據(jù)基地址(以偏移量offset)計(jì)算。我們先來(lái)看看下面的一段簡(jiǎn)單的程序,對(duì)于此程序的分析會(huì)在第2部分結(jié)構(gòu)體成員變量?jī)?nèi)存對(duì)齊中詳細(xì)說明。 

  1. #include<stdio.h>  
  2. int main()  
  3.  
  4.     struct    //聲明結(jié)構(gòu)體char_short_long  
  5.     {  
  6.         char  c;  
  7.         short s;  
  8.         long  l;  
  9.     }char_short_long; 
  10.     struct    //聲明結(jié)構(gòu)體long_short_char  
  11.     {  
  12.         long  l;  
  13.         short s;  
  14.         char  c;  
  15.     }long_short_char;  
  16.     struct    //聲明結(jié)構(gòu)體char_long_short  
  17.     { 
  18.         char  c;  
  19.         long  l;  
  20.         short s;  
  21.     }char_long_short;  
  22. printf(" \n");  
  23. printf(" Size of char   = %d bytes\n",sizeof(char));  
  24. printf(" Size of shrot  = %d bytes\n",sizeof(short));  
  25. printf(" Size of long   = %d bytes\n",sizeof(long));  
  26. printf(" \n");  //char_short_long  
  27. printf(" Size of char_short_long       = %d bytes\n",sizeof(char_short_long));  
  28. printf("     Addr of char_short_long.c = 0x%p (10進(jìn)制:%d)\n",&char_short_long.c,&char_short_long.c);  
  29. printf("     Addr of char_short_long.s = 0x%p (10進(jìn)制:%d)\n",&char_short_long.s,&char_short_long.s);  
  30. printf("     Addr of char_short_long.l = 0x%p (10進(jìn)制:%d)\n",&char_short_long.l,&char_short_long.l);  
  31. printf(" \n");  
  32. printf(" \n");  //long_short_char  
  33. printf(" Size of long_short_char       = %d bytes\n",sizeof(long_short_char));  
  34. printf("     Addr of long_short_char.l = 0x%p (10進(jìn)制:%d)\n",&long_short_char.l,&long_short_char.l);  
  35. printf("     Addr of long_short_char.s = 0x%p (10進(jìn)制:%d)\n",&long_short_char.s,&long_short_char.s);  
  36. printf("     Addr of long_short_char.c = 0x%p (10進(jìn)制:%d)\n",&long_short_char.c,&long_short_char.c);  
  37. printf(" \n");  
  38. printf(" \n");  //char_long_short  
  39. printf(" Size of char_long_short       = %d bytes\n",sizeof(char_long_short));  
  40. printf("     Addr of char_long_short.c = 0x%p (10進(jìn)制:%d)\n",&char_long_short.c,&char_long_short.c);  
  41. printf("     Addr of char_long_short.l = 0x%p (10進(jìn)制:%d)\n",&char_long_short.l,&char_long_short.l);  
  42. printf("     Addr of char_long_short.s = 0x%p (10進(jìn)制:%d)\n",&char_long_short.s,&char_long_short.s);  
  43. printf(" \n");  
  44. return 0;  

程序的運(yùn)行結(jié)果如下(注意:括號(hào)內(nèi)的數(shù)據(jù)是成員變量的地址的十進(jìn)制形式):

2. 結(jié)構(gòu)體成員變量?jī)?nèi)存對(duì)齊

首先,我們來(lái)分析一下上面程序的運(yùn)行結(jié)果。前三行說明在我的程序中,char型占1個(gè)字節(jié),short型占2個(gè)字節(jié),long型占4個(gè)字節(jié)。char_short_long、long_short_char和char_long_short是三個(gè)結(jié)構(gòu)體成員相同但是成員變量的排列順序不同。并且從程序的運(yùn)行結(jié)果來(lái)看,  

  1. Size of char_short_long = 8 bytes  
  2. Size of long_short_char = 8 bytes  
  3. Size of char_long_short = 12 bytes //比前兩種情況大4 byte !  

并且,還要注意到,1 byte (char)+ 2 byte (short)+ 4 byte (long) = 7 byte,而不是8 byte。

所以,結(jié)構(gòu)體成員變量的放置順序影響著結(jié)構(gòu)體所占的內(nèi)存空間的大小。一個(gè)結(jié)構(gòu)體變量所占內(nèi)存的大小不一定等于其成員變量所占空間之和。如果一個(gè)用戶程序或者操作系統(tǒng)(比如uC/OS-II)中存在大量結(jié)構(gòu)體變量時(shí),這種內(nèi)存占用必須要進(jìn)行優(yōu)化,也就是說,結(jié)構(gòu)體內(nèi)部成員變量的排列次序是有講究的。

結(jié)構(gòu)體成員變量到底是如何存放的呢?

在這里,我就不賣關(guān)子了,直接給出如下結(jié)論,在沒有#pragma pack宏的情況下:

原則1 結(jié)構(gòu)(struct或聯(lián)合union)的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小的整數(shù)倍開始(比如int在32位機(jī)為4字節(jié),則要從4的整數(shù)倍地址開始存儲(chǔ))。

原則2 結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補(bǔ)齊。

*原則3 結(jié)構(gòu)體作為成員時(shí),結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ)。(struct a里存有struct b,b里有char,int,double等元素時(shí),那么b應(yīng)該從8的整數(shù)倍地址處開始存儲(chǔ),因?yàn)閟izeof(double) = 8 bytes)

這里,我們結(jié)合上面的程序來(lái)分析(暫時(shí)不討論原則3)。

先看看char_short_long和long_short_char這兩個(gè)結(jié)構(gòu)體,從它們的成員變量的地址可以看出來(lái),這兩個(gè)結(jié)構(gòu)體符合原則1和原則2。注意,在 char_short_long的成員變量的地址中,char_short_long.s的地址是1244994,也就是說,1244993是“空的”,只是被“占位”了!

再看看char_long_short這個(gè)結(jié)構(gòu)體,char_long_short的地址分布情況如下表:

成員變量
成員變量十六進(jìn)制地址
成員變量十進(jìn)制地址
char_long_short.c
0x0012FF2C
1244972
char_long_short.l
0x0012FF30
1244976
char_long_short.s
0x0012FF34
1244980

可見,其內(nèi)存分布圖如下,共12 bytes:

地址
1244972
1244973
1244974
1244975
1244976
1244977
1244978
1244979
1244980
1244981
1244982
1244983
成員
.c
     
.l
.s
   

首先,1244972能被1整除,所以char_long_short.c放在1244972處沒有問題(其實(shí),就char型成員變量自身來(lái)說,其放在任何地址單元處都沒有問題),根據(jù)原則1,在之后的1244973~1244975中都沒有能被4(因?yàn)閟izeof(long)=4bytes)整除的,1244976能被4整除,所以char_long_short.l應(yīng)該放在1244976處,那么同理,最后一個(gè).s(sizeof(short)=2 bytes)是應(yīng)該放在1244980處。

是不是這樣就結(jié)束了?不是,還有原則2。根據(jù)原則2的要求,char_long_short這個(gè)結(jié)構(gòu)體所占的空間大小應(yīng)該是其占內(nèi)存空間最大的成員變量的大小的整數(shù)倍。如果我們到此就結(jié)束了,那么char_long_short所占的內(nèi)存空間是1244972~1244981共計(jì)10bytes,不符合原則2,所以,必須在最后補(bǔ)齊2個(gè) bytes(1244982~1244983)。

至此,一個(gè)結(jié)構(gòu)體的內(nèi)存布局完成了。

下面我們按照上述原則,來(lái)驗(yàn)證這樣的分析是不是正確。按上面的分析,地址單元1244973、1244974、1244975以及1244982、1244983都是空的(至少char_long_short未用到,只是“占位”了)。如果我們的分析是正確的,那么,定義這樣一個(gè)結(jié)構(gòu)體,其所占內(nèi)存也應(yīng)該是12 bytes: 

  1. struct //聲明結(jié)構(gòu)體char_long_short_new  
  2.  
  3. char c;  
  4. char add1; //補(bǔ)齊空間  
  5. char add2; //補(bǔ)齊空間  
  6. char add3; //補(bǔ)齊空間  
  7. long l;  
  8. short s;  
  9. char add4; //補(bǔ)齊空間  
  10. char add5; //補(bǔ)齊空間  
  11. }char_long_short_new; 

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

可見,我們的分析是正確的。至于原則3,大家可以自己編程驗(yàn)證,這里就不再討論了。

所以,無(wú)論你是在VC6.0還是Keil C51,還是Keil MDK中,當(dāng)你需要定義一個(gè)結(jié)構(gòu)體時(shí),只要你稍微留心結(jié)構(gòu)體成員變量?jī)?nèi)存對(duì)齊這一現(xiàn)象,就可以在很大程度上節(jié)約MCU的RAM。這一點(diǎn)不僅僅應(yīng)用于實(shí)際編程,在很多大型公司,比如IBM、微軟、百度、華為的筆試和面試中,也是常見的。

這三大塊硬骨頭是學(xué)習(xí)C語(yǔ)言的絆腳石,下功夫拿掉基本上C語(yǔ)言的大動(dòng)脈就打通了,那么再去學(xué)習(xí)別的內(nèi)容就相對(duì)比較簡(jiǎn)單了。編程學(xué)習(xí)過程中越是痛苦的時(shí)候,學(xué)到的東西就會(huì)越多,克服過去就會(huì)自己的技能,放棄了前面的付出的時(shí)間都將清零。越是難學(xué)的語(yǔ)言在入門之后,在入門之后越覺得過癮,而且還容易上癮。你上癮了沒?還是放棄了? 

 

責(zé)任編輯:龐桂玉 來(lái)源: 良許Linux
相關(guān)推薦

2022-03-16 10:14:55

C語(yǔ)言C++

2020-10-15 09:00:00

Python編程語(yǔ)言

2018-09-29 09:47:41

開源分布式數(shù)據(jù)庫(kù)

2018-06-15 10:39:39

數(shù)據(jù)技術(shù)圖譜

2020-09-22 14:52:33

華為禁令開發(fā)

2017-12-22 22:33:04

游戲語(yǔ)音音視頻社交

2020-03-12 11:52:04

工業(yè)互聯(lián)網(wǎng)新基建物聯(lián)網(wǎng)

2010-07-23 13:07:39

Perl

2016-12-28 10:49:36

5G無(wú)線光纖

2019-09-08 23:21:45

工業(yè)物聯(lián)網(wǎng)IIOT物聯(lián)網(wǎng)

2022-02-19 22:47:46

編程語(yǔ)言開發(fā)C++

2025-11-05 01:47:00

2013-11-11 09:47:49

2009-08-04 18:01:35

ASP.NET代碼塊

2011-05-24 17:34:38

嵌入式系統(tǒng)

2019-01-10 15:16:20

AI數(shù)據(jù)科技

2014-03-18 10:19:30

2011-01-14 13:13:23

嵌入式Linux開發(fā)

2020-04-22 11:51:41

物聯(lián)網(wǎng)嵌入式編程IOT
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

亚洲婷婷在线视频| 理论片日本一区| 亚洲精品小视频| 99视频精品免费| 天天综合视频在线观看| 国产酒店精品激情| 日本高清不卡的在线| 女同久久另类69精品国产| av不卡一区| 在线免费观看日本一区| 狠狠干视频网站| 国产在线一在线二| 国产不卡视频在线播放| 国产成人av在线播放| 9999热视频| 九九热精品视频在线观看| 884aa四虎影成人精品一区| 欧美日本视频在线观看| 麻豆视频网站在线观看| 久久午夜电影网| 91在线视频九色| 久久精品视频2| 国产精品第十页| 中文字幕在线观看日韩| 亚洲一区二区三区无码久久| 久久久久亚洲精品中文字幕| 欧美视频一二三区| 国产精品丝袜久久久久久消防器材| 国产婷婷视频在线| 中文字幕第一区综合| 国产精品sss| 99er热精品视频| 天堂久久久久va久久久久| 欧美激情2020午夜免费观看| 日韩在线视频免费看| 欧美综合在线视频观看| 亚洲精品二三区| 野花视频免费在线观看| 色综合视频一区二区三区44| 色天天综合色天天久久| 国产手机免费视频| 精品国产丝袜高跟鞋| 国产欧美日韩亚州综合| 免费成人看片网址| 亚洲人妻一区二区| 不卡的电视剧免费网站有什么| 91国产丝袜在线放| 国产男女裸体做爰爽爽| 理论电影国产精品| 国产精品一区二区久久久| 久久精品无码av| 国产精品呻吟| 欧美一区视频在线| 男女视频免费看| 日韩午夜免费| 97超碰蝌蚪网人人做人人爽| 日本在线视频中文字幕| 亚洲成人中文| 91地址最新发布| 中文字幕第15页| 亚洲一区一卡| 国产99久久久欧美黑人| 看黄色一级大片| 日韩电影网1区2区| 国产精品天天狠天天看| 夜夜骚av一区二区三区| 精久久久久久久久久久| 亚洲字幕一区二区| 性猛交xxxx乱大交孕妇印度| 成人午夜激情在线| 久久99热只有频精品91密拍| 欧美在线一卡| 欧美极品美女视频| 在线码字幕一区| 成人免费高清| 亚洲大片在线观看| 成人观看免费完整观看| 日韩av超清在线观看| 欧美日韩亚州综合| 免费在线观看日韩av| 国产成人一二片| 日韩黄色在线免费观看| 免费看91的网站| 97精品视频在线看| 欧美激情精品久久久久久大尺度 | 亚洲天堂中文网| 久久99久久99精品免视看婷婷| 亚洲精品欧美一区二区三区| 人妻精品无码一区二区| 26uuu国产日韩综合| 亚洲激情图片| 欧洲在线视频| 91久久久免费一区二区| 久久久精品视频国产| 国产欧美一区二区三区米奇| 在线国产精品视频| 日韩a级片在线观看| 亚洲一区久久| 91久久国产自产拍夜夜嗨| 三区在线观看| 亚洲欧美怡红院| 久久久亚洲精品无码| 国产精品第一国产精品| 亚洲精品在线免费播放| 国产毛片欧美毛片久久久| 午夜日韩福利| 国产精品国产三级国产aⅴ浪潮| 国产欧美日韩综合精品一区二区三区| 99精品1区2区| 在线播放 亚洲| 最近高清中文在线字幕在线观看1| 欧美私模裸体表演在线观看| 图片一区二区| 欧美日韩亚洲另类| 日本三级日本三级日本三级极| 免费看成人哺乳视频网站| 久久精品福利视频| 色屁屁影院www国产高清麻豆| 经典一区二区三区| 日本一区免费看| 97影院秋霞午夜在线观看| 色婷婷综合久久久中文一区二区| 精品国产午夜福利在线观看| 精品香蕉视频| 欧美性受xxxx白人性爽| 国产黄a三级三级看三级| 国产女主播一区| 欧美,日韩,国产在线| 亚洲免费一区三区| 久久视频国产精品免费视频在线| 黄色av网站免费观看| av不卡免费在线观看| 青青草原网站在线观看| 成人精品国产亚洲| 亚洲视频在线免费观看| 久久免费激情视频| 成人aaaa免费全部观看| 国产激情片在线观看| 91精品影视| 亚洲精选中文字幕| 国产视频91在线| 99在线精品一区二区三区| 国产精品视频二| 精品一区二区三区四区五区| 日韩网站免费观看高清| 中文字幕 国产精品| 久久一日本道色综合| 日韩免费视频播放| 神马精品久久| 久久国产成人精品| 欧洲美女7788成人免费视频| 国产成人手机在线| 一区二区国产视频| 日韩高清一二三区| 欧美日韩国产免费观看| 3d精品h动漫啪啪一区二区| 丝袜美腿美女被狂躁在线观看| 在线区一区二视频| jizz18女人高潮| 日产国产高清一区二区三区| 无遮挡亚洲一区| 成人黄色在线| 久久亚洲精品中文字幕冲田杏梨| 国产精品久久免费| 亚洲精品日韩一| 1314成人网| 欧美日韩三区| 国产一区二区无遮挡| 超碰成人av| 亚洲欧美日韩中文在线制服| 无码人妻av一区二区三区波多野 | 国产精品久久久久久久9999| 国产福利小视频在线观看| 欧美无砖砖区免费| 色老板免费视频| 国产精品18久久久| 国产免费观看高清视频| 欧美军人男男激情gay| 国产在线观看一区二区三区| av中文字幕在线播放| 亚洲第一网中文字幕| 精品国产一区二区三区四| 久久久久久久久久美女| 久久这里只精品| 国模吧视频一区| 欧美国产综合视频| 国产亚洲人成a在线v网站| 欧美成人亚洲成人| 全色精品综合影院| 777xxx欧美| 国产精品7777777| 国产欧美精品一区| 爱情岛论坛亚洲自拍| 免费一级欧美片在线播放| 亚洲一区免费看| 老牛国内精品亚洲成av人片| 国产精品爽黄69天堂a| 懂色av一区| 中文字幕日本精品| 日韩在线视频免费| 欧美日韩精品综合在线| 国产精品成人国产乱| 中文字幕不卡在线观看| 中文字幕视频观看| 另类综合日韩欧美亚洲| 水蜜桃色314在线观看| 三级电影一区| 久久亚洲一区二区| 亚洲三区欧美一区国产二区| 日韩免费观看高清| 丁香高清在线观看完整电影视频| 日韩中文在线中文网三级| 五月天婷婷在线观看| 91精品国产手机| 国产精华7777777| 婷婷成人激情在线网| 日本高清一二三区| 国产欧美视频在线观看| 国产毛片毛片毛片毛片毛片毛片| 精品亚洲免费视频| 亚洲狼人综合干| 一区二区福利| 蜜臀精品一区二区| 无需播放器亚洲| 欧洲av一区| 老汉色老汉首页av亚洲| 鬼打鬼之黄金道士1992林正英| 久久久久久久久久久免费视频| 国产精品传媒精东影业在线| 欧美三级网色| 欧美挤奶吃奶水xxxxx| 91嫩草视频在线观看| 日韩欧美精品一区二区综合视频| 91精品国产高清| 国产探花视频在线观看| 欧美成人激情图片网| 午夜在线免费观看视频| 亚洲日本aⅴ片在线观看香蕉| 神宫寺奈绪一区二区三区| 欧美不卡视频一区| 国产激情无套内精对白视频| 91精品国产手机| 国产伦精品一区二区三区四区| 欧美午夜电影一区| 怡红院男人天堂| 欧美色涩在线第一页| 青青草视频在线观看免费| 色吊一区二区三区| 一级片中文字幕| 欧美性猛交xxxxx免费看| 91美女免费看| 欧美性xxxxxxxxx| 国产精品不卡av| 午夜电影网一区| 国产 欧美 日韩 在线| 精品成人av一区| 9i看片成人免费看片| 色欧美乱欧美15图片| 免费在线不卡av| 欧美日韩一区二区电影| 在线观看色网站| 欧美丰满美乳xxx高潮www| 国产模特av私拍大尺度| 精品国产乱码久久久久久免费| 人人妻人人玩人人澡人人爽| 日韩av影片在线观看| 欧洲毛片在线| 日韩在线一区二区三区免费视频| v天堂福利视频在线观看| 欧美日韩999| 手机在线观看av| 国产精品成人一区二区三区吃奶| 国产第一亚洲| 91在线在线观看| 日韩高清影视在线观看| 日韩高清av| 欧美在线视屏| 日韩精品视频久久| 捆绑调教一区二区三区| 日本成人在线免费| 91视频免费播放| 任我爽在线视频| 午夜精彩视频在线观看不卡| 国产在线一级片| 日韩一区二区在线观看视频| 色噜噜一区二区三区| 国产香蕉97碰碰久久人人| 91cn在线观看| 欧美在线视频一区二区| 在线欧美激情| 精品一区二区三区自拍图片区 | 国产亚洲自拍av| 欧美性20hd另类| 99免费在线视频| 亚洲欧美国产一本综合首页| 九七久久人人| 欧美中文在线视频| 美国十次综合久久| 欧美日韩另类丝袜其他| 欧美在线1区| 成人一区二区三| 懂色中文一区二区在线播放| 91精品久久久久久久久久久久| 亚洲综合网站在线观看| 在线播放亚洲精品| 亚洲欧美国产精品| 视频在线观看入口黄最新永久免费国产 | 欧美日韩中文精品| 日本毛片在线观看| 真人bbbbbbbbb毛片| 日韩激情视频网站| wwwww在线观看| 中文一区在线播放| 日韩精品一区二区不卡| 欧美老女人第四色| 欧美日韩伦理片| 欧美激情视频一区| 久久亚洲人体| 久久久久久国产精品mv| 欧美极品一区二区三区| 五月婷婷狠狠操| 91视频免费播放| 久久久久久久久99| 91精品中文字幕一区二区三区 | 日韩精品一区二区在线观看| 国产在线小视频| 97精品视频在线观看| 欧美h版在线观看| 亚洲综合视频一区| 日韩国产成人精品| 无遮挡aaaaa大片免费看| 亚洲精品成人在线| 99热这里只有精品3| 在线精品高清中文字幕| 肉色欧美久久久久久久免费看| 国产手机精品在线| 伊人成年综合电影网| 在线观看网站黄| 亚洲美女免费视频| 国产三级精品在线观看| www.日韩av.com| 日本国产亚洲| 中文字幕欧美人与畜| 久久成人综合网| 中文字幕91视频| 欧美日韩一区二区三区不卡 | 一区二区三区视频免费视频观看网站| 在线观看成人一级片| 精品一二三四区| 欧美色图亚洲视频| 日韩一二三区不卡| 欧美亚洲系列| 国产欧美一区二区三区另类精品 | 欧美先锋资源| 亚洲人辣妹窥探嘘嘘| 国产精品三级电影| 91精品在线视频观看| 中文字幕欧美视频在线| 成人看片毛片免费播放器| 一本一本a久久| 国产一区二区三区四区五区美女| 天天操天天操天天操天天操天天操| 91精品免费观看| 九色91在线| 久久精品久久精品国产大片| 石原莉奈一区二区三区在线观看| 一区二区三区在线观看免费视频| 欧美日韩性生活| 欧美亚洲天堂| 欧美国产一二三区| 麻豆国产精品视频| 国产va在线播放| 亚洲成人性视频| 人人鲁人人莫人人爱精品| 一区二区在线观看网站| 国产精品夜夜爽| 久久国产视频播放| 中文字幕亚洲一区二区三区五十路| 91精品福利观看| 91午夜在线观看| 久久精品亚洲精品国产欧美kt∨| 在线观看视频二区| 久久露脸国产精品| 精品久久久久久久久久久下田| 91pony九色| 欧美日韩免费看| 麻豆视频在线| 精品久久久久久中文字幕动漫| 免费观看日韩电影| 国产在线视频二区| 在线看福利67194| 日韩免费高清视频网站| 日韩av资源在线| 亚洲免费在线视频| 久久久pmvav| 国产91亚洲精品一区二区三区| 久久中文在线| 精品人妻在线播放| 最近2019年好看中文字幕视频| 国产一级成人av|