0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C語言在單片機(jī)中是如何執(zhí)行的

科技綠洲 ? 來源:網(wǎng)絡(luò)整理 ? 作者:網(wǎng)絡(luò)整理 ? 2023-11-24 15:45 ? 次閱讀

或許我們平時大多數(shù)學(xué)習(xí)C語言都是在Windows環(huán)境下學(xué)習(xí)的,對于程序執(zhí)行的底層邏輯了解的不是非常清楚,所以本文在這里給大家介紹一下,C語言在單片機(jī)中是如何執(zhí)行的。

Part1CPU與外設(shè)

我們知道,單片機(jī)也是有CPU的,它負(fù)責(zé)執(zhí)行代碼,運(yùn)算數(shù)據(jù),以及發(fā)出控制信號等功能,而與CPU直接相連的設(shè)備我們稱之為外設(shè)(就是集成芯片)。

本文以STM32F103ZET6為例來講解,該芯片使用的是ARM架構(gòu),該架構(gòu)采用的是哈弗結(jié)構(gòu)。

  • 哈弗結(jié)構(gòu):內(nèi)存和外設(shè)統(tǒng)一編址。

ARM芯片屬于精簡指令集計(jì)算機(jī)(RISC:Reduced Instruction Set Computing),它所用的指令比較簡單,有如下特點(diǎn):

  1. 對內(nèi)存只有讀、寫指令;
  2. 對于數(shù)據(jù)的運(yùn)算是在CPU內(nèi)部實(shí)現(xiàn);
  3. 使用RISC指令的CPU復(fù)雜度小一點(diǎn),易于設(shè)計(jì)。

比如對于a=a+b這樣的算式,需要經(jīng)過下面4個步驟才可以實(shí)現(xiàn):

圖片細(xì)看這幾個步驟,有些疑問,a的值讀出來后保存在CPU里面哪里?b的值讀出來后保存在CPU里面哪里?a+b的結(jié)果又保存在哪里?

圖片如上圖所示,CPU也是由多個部分組成的,包括ALU邏輯運(yùn)算單元,控制單元,以及多個寄存器等等。

假設(shè)變量a的地址是0x12,變量b的地址是0x34,第一步的匯編代碼LDR R0, [a]的意思就是將0x12地址中的值讀取到R0寄存器中,第二步讀取b變量同理。

  • LDR + 第一操作數(shù) + 第二操作數(shù):就是將第二操作數(shù)的值賦第一操作數(shù)。

當(dāng)變量a和變量b都被讀到了CPU的寄存器中后,執(zhí)行第三步匯編代碼ADDR R0, R0, R1,意思是將R0和R1中的值相加,然后將結(jié)果保存到R0中。

  • ADD:相加的匯編指令,可以有三個操作數(shù)也可以有兩個操作數(shù),三個操作數(shù)則后兩個操作數(shù)相加,得的結(jié)構(gòu)均保存到第一個操作數(shù)。

最后就是將R0中的計(jì)算結(jié)果再寫回到內(nèi)存中,執(zhí)行第四步匯編代碼STR R0,[a],意思是將R0中的值寫入到變量a的地址處0x12。

圖片如上圖所示,由于有32根地址線,所以CPU可訪問的地址范圍就是0x0000 0000 ~ 0xFFFF FFFF,就拿我們熟知的FlashSRAM來說,它倆和CPU直接相連,所以也可以看成是外設(shè)。

  • Flash:用來存放用戶燒錄的程序,掉電數(shù)據(jù)不丟失(硬件特性)。
  • SRAM:用來存放程序執(zhí)行過程中的臨時數(shù)據(jù),掉電數(shù)據(jù)丟失。

Flash的地址范圍是0x0800 0000 ~ 0x0807 FFFF,SRAM的地址范圍是0x2000 0000 ~ 0x2000 FFFF,這是我們根據(jù)上面的圖才知道的。

但是對于CPU而言,它并不知道哪里是FLASH哪里是SRAM,它只是被動地在執(zhí)行代碼。CPU在一上電以后就從0x0000 0000處開始執(zhí)行代碼(可以進(jìn)行設(shè)置,以后再講解),直到調(diào)用了我們C代碼中必須有的main函數(shù),然后進(jìn)入我們自己的邏輯當(dāng)中。

1.1 Flash

圖片如上圖啟動文件所示,CPU會通過BL匯編語句來調(diào)用main函數(shù),但是在這之前,還會執(zhí)行LDR匯編語句來給棧頂指針SP賦值。

  • BL:跳轉(zhuǎn)指令,也就是讓程序跳轉(zhuǎn)到指定位置處執(zhí)行,相當(dāng)于函數(shù)調(diào)用。

我們知道,代碼最終會被轉(zhuǎn)換成機(jī)器碼讓CPU去執(zhí)行,而存放這些機(jī)器碼也需要空間,所以代碼也是有地址的。

圖片如上圖所示,無論是調(diào)用main函數(shù)之前的匯編代碼,還是main函數(shù)的代碼,它們的地址都是0x0800 0xxx,距離FLASH的起始地址0x0800 0000不是很遠(yuǎn),說明我們燒錄到單片機(jī)中的代碼就是存放在FLASH中的。

  • 無論是main中的代碼,還是前面的匯編代碼,只要是從FLASH起始處開始的,都屬于我們程序員寫的代碼。
  • 芯片廠家在FLASH起始地址之前,固化了一些代碼,這個暫不作說明。

1.2 SRAM(內(nèi)存)

1.2.1 棧

當(dāng)main執(zhí)行起來以后,運(yùn)算數(shù)據(jù)得到的臨時結(jié)果或者中間數(shù)據(jù)就都會暫存到SRAM上,也就是我們平常所說的內(nèi)存中。

圖片

如上圖所示,在使用BL調(diào)用main函數(shù)之前,還使用了LDR給棧頂指針SP賦了初值,紅色箭頭指向的位置就是棧頂指針指向的位置。

代碼中的局部變量,函數(shù)棧幀等等數(shù)據(jù),全部都存放在SP開始往下的位置,因?yàn)?棧的開辟是從高地址向低地址 。

圖片

如上圖所示,在main函數(shù)中創(chuàng)建兩個變量a和b,加volatile的作用是防止編譯器將這兩個變量優(yōu)化掉導(dǎo)致在這里無法演示現(xiàn)象。

  • main函數(shù)也是被調(diào)用的,所以在其內(nèi)部創(chuàng)建的變量也屬于局部變量,局部變量就統(tǒng)統(tǒng)存放在棧上。

匯編代碼中,在創(chuàng)建變量a之前先執(zhí)行了一句PUSH {r2-r3,lr}匯編語句,意思是將寄存器lr,寄存器r2r3中的值壓入棧中。

  • lr:寄存器存放的是函數(shù)的返回地址,其實(shí)就是CPU中的r15寄存器。
  • PUSH:執(zhí)行壓棧操作,將數(shù)據(jù)壓入到棧中后,棧頂指針向下移動。

此時向棧中壓入了三個個數(shù)據(jù),每個數(shù)據(jù)都是4字節(jié)的,所以SP向下移動了12個字節(jié),這12個字節(jié)就可以看作當(dāng)前main函數(shù)的棧幀大小。

圖片

如上圖,當(dāng)執(zhí)行到給變量a賦值1時,執(zhí)行了匯編代碼MOVS r0,#0x01,表示將數(shù)值1賦值給寄存器r0。然后再執(zhí)行匯編代碼STR r0,[sp,#0x04],表示將寄存器r0中的值,寫入到sp + 0x04地址處。

  • MOVS:將后一個操作數(shù)賦值給前一個操作數(shù)。

給變量b賦值2的時候,原理同上。所以此時在內(nèi)存中就存在了1和2兩個值,分別存在于sp+4sp+0的位置處,后面用到變量a和b的時候,也是通過棧頂指針sp來找這兩個值。

在這個過程中我們發(fā)現(xiàn),寄存器r2r3的的作用就是 占坑 ,現(xiàn)在棧中給變量a和b占兩個位置,等到STR賦值的時候?qū)⑦@兩個位置覆蓋即可。

那如果我創(chuàng)建100字節(jié)大小的數(shù)組呢?難道用100個寄存器來占坑嗎?顯然不可能,CPU一共也沒那么多寄存器。

圖片

如上圖所示,創(chuàng)建100字節(jié)大小的數(shù)組,先開辟100個字節(jié)大小的??臻g,執(zhí)行匯編語句SUB sp,sp,#0x64,表示用當(dāng)前的sp值減去0X64(100的16進(jìn)制),將結(jié)果再賦值到sp中。

  • SUB:用法和ADD相似,只是作用是后兩個操作數(shù)做減法,得到的結(jié)果賦值給第一個操作數(shù)。

此時在SRAM(內(nèi)存)上就存在一個100字節(jié)大小的棧用來存放這個str數(shù)組,此時它不使用占坑的方式了,而是直接改變SP的值來改變棧區(qū)的大小。

1.2.2 數(shù)據(jù)段

圖片如上圖所示,創(chuàng)建兩個全局變量a和b,還有一個靜態(tài)變量c,在調(diào)試窗口中可以看到,變量a的地址是0x20000 0000,變量b的地址是0x20000 0004,變量c的地址是0x2000 0008,這三個變量緊挨著。

  • 在C語言學(xué)習(xí)中我們知道,全局變量和靜態(tài)變量是存放在數(shù)據(jù)段的。
  • 先忽略為什么它們的初始值都是0這個問題。

在本文最前面放了一張內(nèi)存地址映射圖,其中SRAM的地址范圍是0x2000 0000 ~ 0x20000 FFFF,也就是說內(nèi)存的起始地址就是0x2000 0000,而變量a,b,c從起始位置開始存放,所以說這個位置就是數(shù)據(jù)段起始位置。

圖片

如上圖所示,當(dāng)給變量a賦值時,先執(zhí)行MOVS r0,#0x01,將數(shù)值1賦值給寄存器r0,然后執(zhí)行LDR r1,[pc,#20]語句,表示從PC + 20的地址處讀取數(shù)據(jù)放入到寄存器r1中。

  • PC:程序計(jì)數(shù)器,實(shí)際上就是CPU寄存器中的R15,它存放程序的地址,其值永遠(yuǎn)是當(dāng)前語句的下一條語句的地址。
  • CPU會根據(jù)PC值去執(zhí)行對應(yīng)的指令。

PC + 20的值是0x0800 0016C,這是一個Flash處的地址,而該地址處的值是0x0000,由于LDR一次取四個字節(jié)的數(shù)據(jù),所以要連0x0800 0016E處的值0x2000也要讀走,兩個值按照大端存儲模式復(fù)原(高地址存放高字節(jié)序),得到的值就是0x2000 0000。

所以此時寄存器r1中的值就是0x2000 0000,再執(zhí)行STR r0,[r1,#0x00]匯編語句,將r0中的1寫入到0x20000 0000處,也就是數(shù)據(jù)段變量a的地址處,此時就成功改變了它的值。

1.2.3 堆

圖片如上圖,整個SRAM上,棧占用一部分空間,它的大小隨著的SP的變化而變化,數(shù)據(jù)段占用一部分空間,但是還沒有全部使用完畢,還有剩余的空閑空間,就建立在這部分空間上。

  • 堆空間的大小并不會發(fā)生變化,它就是一塊固定大小的空間,用戶可以去申請使用,用完了還必須歸還。

所以可以用一個大的全局?jǐn)?shù)組來管理這塊空間,因?yàn)槿謹(jǐn)?shù)組存放在數(shù)據(jù)段,它的大小并不會隨著SP的變化而變化,從而堆空間的大小也不會變化。

  • 雖然叫做堆,但是這部分空間仍然屬于數(shù)據(jù)段,只是提供了接操作這部分空間的接口。

圖片如上圖所示,在此定義了一個全局?jǐn)?shù)組char buffer[500]來充當(dāng)堆,還有一個全局的index用來記錄堆的使用情況,又實(shí)現(xiàn)了一個mymalloc用來向堆區(qū)申請空間。

圖片

全局?jǐn)?shù)組buffer的地址是0x2000 0010,排在a,b,c,index后面,第一次mymalloc以后,得到的地址是0x2000 0010,大小是100個字節(jié),第二次mymalloc以后,得到的地址是0x2000 0074,地址相差0x64也就是100,說明這是在第一次申請的基礎(chǔ)上再次申請的。index的值是0x12C也就是300,說明一共申請了300個字節(jié)的空間。

自定義的釋放函數(shù)myfree在此就不寫了,各位小伙伴可以自行嘗試。所以說, 堆本質(zhì)上就是就是一塊空閑內(nèi)存,可以使用malloc/free函數(shù)來管理它

為什么Flash的起始地址就是0x0800 0000,SRAM的起始地址就是0x2000 0000?不能是別的嗎?

圖片

如上圖所示,在MDK中,連接器選項(xiàng)中R/O BaseFlash基地址,用來設(shè)置Flash的起始地址,R/W BaseSRAM基地址,用來設(shè)置SRAM的起始地址。

下面藍(lán)色框中的是連接器控制信息,里面的內(nèi)容是我們程序員寫的,目的是告訴連接器要做什么。

默認(rèn)情況下,紅色框中的SRAM起始地址是0x2000 0000,本文將其改成了0x2000 8000,來看一下會發(fā)生什么?

圖片如上圖所示,此時代碼里只有一個全局變量a,它位于數(shù)據(jù)段的起始位置,也就是SRAM的起始位置,其地址是0x2000 8000,本文成功地修改了SRAM的起始地址。

Flash的地址也是同理,也可以通過連接器R/O Base進(jìn)行修改。

Part2變量的初始化

  • 變量:能改變的量,它一定在內(nèi)存上占據(jù)空間,

2.1 局部變量

圖片如上圖所示,在main函數(shù)中創(chuàng)建了局部變量a并賦值0x11223344,創(chuàng)建了局部變量b并賦值0x11。在匯編代碼中,首先移動SP,由于只有兩個變量,所以壓棧r2r3來占位。

初始化變量a的時候,先執(zhí)行LDR r0,[pc,#12]匯編語句,取地址為0x0800140Flash中取值,讀取了該地址及下個地址供四個字節(jié)數(shù)據(jù)0x11223344,賦值給寄存器r0。然后再執(zhí)行STR r0,[sp,#0x04]匯編語句,將r0中的0x11223344賦值給變量a所在處。

初始化變量b的時候,先執(zhí)行MOVS r0,#0x11匯編語句,直接將立即數(shù)#0x11賦值給寄存器r0,然后再執(zhí)行STR r0,[sp,#0x00]匯編語句,將r0中的0x11賦值給變量b所在處。

  • 兩個局部變量的初始化過程并不一樣,初始值為4字節(jié)的變量需要去Flash中取初值,初始值為1字節(jié)的變量,直接就給賦值了。

指令也是有大小的,如0x08000132 4803 LDR r0,[pc,#12]中,0x08000132是代碼所在的Flash地址,4803是代碼匯編之后的機(jī)器碼,大小是2字節(jié)(CPU執(zhí)行的是機(jī)器碼,匯編語句是為了方便我們看的,剩下的就是匯編語句)。

對于初始值為0x#11的初始化,兩個字節(jié)的指令足夠容納一個字節(jié)的初值,所以直接就賦值初始化了。

對于初始值為0x11223344的初始化,兩個字節(jié)的指令無法容納四個字節(jié)的初值,所以必須取Flash中取初值到寄存器中,然后再進(jìn)行賦值。

圖片如上圖,創(chuàng)建一個char buffer[500]數(shù)組全部用1初始化,使用BL.W指令跳轉(zhuǎn)到__aeabi_memclr4處進(jìn)行初始化,相當(dāng)于調(diào)用了一個函數(shù)來初始化這個數(shù)組,這個函數(shù)是由編譯器生成的,也是一堆匯編語句,這里暫不做介紹。

圖片如上圖,當(dāng)main函數(shù)執(zhí)行完,執(zhí)行了return 0以后,會執(zhí)行POP {r2-r3,pc}匯編語句,將前面壓棧時向下生長的空間回收,也就是SP向上移動。

  • POP:出棧操作,將棧中的數(shù)據(jù)彈出,并且SP棧頂指針向上移動。

此時原本存放變量a和b的空間就位于棧外面了,原本的值彈出給了r0r1PC拿到函數(shù)的返回地址lr。

雖然a和b的內(nèi)存空間還存在,但是已經(jīng)不再被維護(hù)了,當(dāng)有新的局部變量需要棧的時候,SP會重新向下移動,并且使用新的值覆蓋掉這部分空間。

2.2 全局變量和靜態(tài)變量

圖片

如上圖所示,定義兩個全局變量a和b,初始值分別為10和20,定義一個全局靜態(tài)變量,初始值為30,定義一個局部靜態(tài)變量,初始值為40,當(dāng)程序執(zhí)行到main中時,通過調(diào)試窗口看到它們的值都是0,并沒有被初始化。

圖片如上圖,在啟動文件中使用BL跳轉(zhuǎn)到main函數(shù)之前,需要先跳轉(zhuǎn)到copy函數(shù),將全局變量的初始值全部復(fù)制到對應(yīng)數(shù)據(jù)段的地址。但是這里并沒有實(shí)現(xiàn)copy函數(shù),所以全局變量沒有被初始化。

  • 全局變量的初始值是存放在Flash中的,注意是只存放初始值,不存放變量名,因?yàn)镃PU執(zhí)行的是機(jī)器碼,機(jī)器碼中并沒有變量名這么一說。

圖片如上圖,仍然是這四個變量,但是在定義都是時候都沒有給初始值,沒有進(jìn)行初始化,但是在調(diào)試窗口看到它們的值仍然是0。

  • 對于沒有初始值的數(shù)據(jù)段變量,在編譯的時候,編譯器會用0將這些變量初始化,也就是將對應(yīng)地址寫0。

相當(dāng)于會調(diào)用一個memset函數(shù)將這部分變量全部初始化為0。這些變量處于數(shù)據(jù)段的 未初始化數(shù)據(jù)段 ,而前面有初始值的處于 已初始化數(shù)據(jù)段

圖片如上圖所示,便是整個數(shù)據(jù)段的內(nèi)存示意圖。

在STM32F103中,代碼是在FLASH中運(yùn)行的,并不會加載到內(nèi)存中,而且代碼和數(shù)據(jù)段的初始值是混合存放在Flash中的。

Part3函數(shù)

圖片如上圖所示,Add函數(shù)其實(shí)就是8條匯編指令,調(diào)用函數(shù)就是讓CPU的PC寄存器等于8條指令的首地址,也就是函數(shù)地址。圖片如上圖,main函數(shù)開辟一次棧,SP位于上圖紅色位置,棧里有變量a和b以及main函數(shù)的返回地址lr。

在調(diào)用Add函數(shù)的時候,會再壓一次棧,SP位于上圖綠色位置,這次壓入了Add函數(shù)的返回地址lr,以及形參v,再執(zhí)行SUB語句為局部變量a開辟空間,SP位于上圖藍(lán)色位置。

  • 函數(shù)傳參通過寄存器r0實(shí)現(xiàn),在PUSH的時候,r0中已經(jīng)有了實(shí)參,然后將實(shí)參壓入調(diào)用函數(shù)的棧中成為形參。

然后執(zhí)行LDRSTR將形參的值拿到局部變量a中,再進(jìn)行加一操作,操作完畢后將結(jié)果再度寫入到形參v的位置,當(dāng)函數(shù)返回時,執(zhí)行LDR將運(yùn)算結(jié)果存入r0寄存器中,然后POP出棧操作,SP重新位于上圖紅色位置。

  • 函數(shù)返回值的時候,同樣通過r0實(shí)現(xiàn),SP雖然向上移動了,但是r0中有返回值。

調(diào)用函數(shù)結(jié)束后,執(zhí)行STRr0中的運(yùn)算結(jié)果寫入到變量b。

圖片如上圖,main函數(shù)在調(diào)用Add_Sum函數(shù)的時候,一次傳入了八個變量,賦了初值以后,將其中的四個變量交給了寄存器r3-r7,然后執(zhí)行STM sp,[r8-r11],將剩下的四個變量繼續(xù)壓棧。

  • STM:一次存儲多個寄存器中的值到指定位置。

在執(zhí)行Add_Sum函數(shù)的時候,執(zhí)行LDM r5,[r5-r7,r12],從棧中將后四個變量取出來,再與寄存器r3-r7中的四個值一起求和,最后將結(jié)果返回。

  • LDM:一次讀取多個值到多個寄存器中。

調(diào)用函數(shù)時,如果傳入的變量比較多,或者是數(shù)組的話,由于沒有那么多的寄存器可以做中間人,所以會將這些變量繼續(xù)壓入調(diào)用方的棧中,被調(diào)用函數(shù)在用的時候從調(diào)用方的棧中拿走進(jìn)行拷貝。

這就是為什么我們在函數(shù)中改變形參,并不影響實(shí)參的原因,因?yàn)樵诤瘮?shù)中形參是實(shí)參的拷貝,它位于函數(shù)的棧中,調(diào)用方的棧并不受影響。

Part4指針變量

圖片如上圖,創(chuàng)建了一個int類型的變量,一個char類型的變量,一個int* 類型的變量,一個char* 類型的變量,從匯編處可以看出,指針變量同樣要在棧中占用空間,只是初始化的時候,指針變量賦值的是地址,如ADD r2,sp,#0x04,就是將棧頂指針向上移動4個字節(jié)后的地址賦值給為int* pa變量占坑的r2。

  • 指針變量仍然是變量,是變量就要占據(jù)內(nèi)存空間,和普通的變量沒有區(qū)別,只是它的值是地址而已。

在訪問這兩個指針變量時,*pa = 20,執(zhí)行了STR r0,[r2,0x00],一次給變量a寫入四個字節(jié),*pb = 'B',執(zhí)行了STRB r0,[r11#0x00],一次給變量b寫入一個字節(jié)。

  • STRB:存儲一個字節(jié)數(shù)據(jù),作用和STR一樣,只是寫入字節(jié)是一個字節(jié)。

訪問不同類型的指針,底層會有不同的策略,讓CPU以對應(yīng)的視角去操作對應(yīng)的內(nèi)存。如*pa,CPU就會認(rèn)為它現(xiàn)在訪問地址處的變量是一個int類型,而不是一個char類型。

圖片如上圖,創(chuàng)建函數(shù)指針變量int(*pf)(volatile int),將函數(shù)Add地址賦值給變量pf。執(zhí)行LDR r4,[pc,#12]Flash0x0800 0158處取函數(shù)地址為0x0800 0131

但是我們看到函數(shù)的8條指令的起始地址是0x0800 0130,與r4中取到的函數(shù)地址相差1,這是因?yàn)樵?code>0x0800 0158處存放的0x0800 0131代表兩層意思。

  • 函數(shù)地址的最低位為1表示該函數(shù)使用的是Thumb指令集,這個1和實(shí)際地址沒有關(guān)系。
  • 該值減去1才是真正的函數(shù)起始地址,也就是0x0800 0130。

無論什么類型的指針變量,它里面存放的都是相應(yīng)變量的首地址,包括函數(shù)指針變量,再通過策略決定CPU讀寫該首地址后面幾個字節(jié)。

Part5結(jié)構(gòu)體和聯(lián)合體

圖片如上圖,創(chuàng)建一個局部結(jié)構(gòu)體變量,有三個成員變量int age,char sex,int score,并且給它們初始化。先執(zhí)行LDR拿到在Flash中存放初始值的地址0x0800 0144r2中,然后再執(zhí)行LDM從初值起始地址開始讀取初值0x0000 18,0x0000 00001,0x0000 0064,對應(yīng)著24,1,100。

  • 結(jié)構(gòu)體初始化時,初值存放在Flash中,需要讀取到寄存器中,然后再賦值給結(jié)構(gòu)體各個成員。

通過調(diào)試窗口查看三個成員的地址,發(fā)現(xiàn)成員之間的地址相差4個字節(jié),其中int ageint score是四字節(jié)變量占用4個空間,但是char sex是一字節(jié)變量也占用四個空間。

如上圖中SRAM示意圖所示,此時sex的四個字節(jié)中只用了一個字節(jié),浪費(fèi)了三個字節(jié)。

  • 為了提高結(jié)構(gòu)體的訪問效率,結(jié)構(gòu)體變量在存放時會進(jìn)行內(nèi)存對齊。

圖片

如上圖,數(shù)據(jù)線和地址線都是32位的,也就是4字節(jié),除此之外還有四根控制線be0,be1,be2,be3。無論是訪問還是寫入,CPU一次操作都是四個字節(jié)的內(nèi)存。

當(dāng)be0有效時,CPU操作4個字節(jié)中第1個字節(jié)的空間,be1有效就操作第2個字節(jié)的空間,be2有效就操作第3個字節(jié)的空間,be3有效就操作第4個字節(jié)的空間。

如果操作的是第一個4字節(jié)中的3個字節(jié)和第二個4字節(jié)的1個字節(jié)組成的四字節(jié)空間,CPU就需要操作兩次,第一次操作時be1,be2,be3有效,第二次操作時be0有效,最后組合得到需要的數(shù)據(jù)。

采用結(jié)構(gòu)體內(nèi)存對齊方案,雖然char sex浪費(fèi)了三個字節(jié)的空間,但是在操作int score的時候,可以一次性操作完畢,不需要第二次。

  • 結(jié)構(gòu)體對齊利用了以空間換時間的思想。

圖片如上圖,創(chuàng)建一個位段結(jié)構(gòu)體,成員agesex都只占用int的32個比特位中的1個比特位,成員score占4個字節(jié)32個比特位。

先執(zhí)行LDR取數(shù)據(jù),然后執(zhí)行BIC r0,r0,#0x01將r0中的32個比特位的第一個比特位清0,然后再執(zhí)行ADDS r0,r0,#1讓第一個比特位的值成為1,此時給int age:1初始化完成。

  • BIC:清除指定比特位,讓該位為0。

同理,再給int sex:1初始化為1,也就是讓32個比特位中的第二個比特位為1。此時還剩下30個比特位被浪費(fèi)掉了,下一個int score占用完整的32個比特位,同樣是為了提高效率。


圖片如上圖,結(jié)構(gòu)體中又增加了一個聯(lián)合體成員union weight,char kgint g兩種類型的變量共用這一個空間。而且可以看到,weight,kg,g三者的地址都是0x2000 FFF8。

在給成員kg賦值80的時候,整個weight空間的值是0x0000 0050,在給成員g賦值的時候,整個weight空間的值是0x0001 3880。操作char類型成員,只改變4個字節(jié)中的一個字節(jié),操作int類型成員,則4個字節(jié)全部改變。

對應(yīng)的匯編代碼中,操作char成員使用的是STRB,操作int成員使用的是STR。

Part6總結(jié)

圖片如上圖便是在這篇文章中講解的ARM架構(gòu)部分模型,以及常用C語言知識在ARM架構(gòu)中是如何體現(xiàn)的。

程序在經(jīng)過預(yù)處理,編譯,匯編,最后再經(jīng)過連接器分配地址形成.axf.bin,或者.hex等類型的文件,這幾種文件中的內(nèi)容全部都是機(jī)器碼。

將最終的機(jī)器碼燒錄到單片機(jī)中,單片機(jī)一上電就開始執(zhí)行這些機(jī)器碼,執(zhí)行過程中是沒有編譯器,電腦系統(tǒng)的參與的,無論是變量的定義,初始化,還是內(nèi)存空間的分配,你還能說是自動完成的嗎?

所以說,當(dāng)程序在單片機(jī)中開始運(yùn)行的時候,它的一切就早被安排好了,就是按照前面所講述的去安排設(shè)計(jì)的,CPU只需要按照機(jī)器碼執(zhí)行即可。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機(jī)
    +關(guān)注

    關(guān)注

    6023

    文章

    44376

    瀏覽量

    628346
  • WINDOWS
    +關(guān)注

    關(guān)注

    3

    文章

    3503

    瀏覽量

    87883
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7575

    瀏覽量

    134048
  • 程序
    +關(guān)注

    關(guān)注

    115

    文章

    3720

    瀏覽量

    80357
收藏 人收藏

    評論

    相關(guān)推薦

    STM32單片機(jī)C語言基礎(chǔ)知識

    C語言單片機(jī)開發(fā)的必備基礎(chǔ)知識,本文列舉了部分STM32學(xué)習(xí)中比較常見的一些C語言基礎(chǔ)知識。
    發(fā)表于 09-21 17:21 ?4435次閱讀

    什么是C語言?單片機(jī)有什么特點(diǎn)?為什么要用C語言編程?

    隨著技術(shù)的發(fā)展,電子產(chǎn)品越來越多,方便了我們的日常生活,大多數(shù)電子產(chǎn)品上都有單片機(jī),而單片機(jī)是通過執(zhí)行軟件邏輯來實(shí)現(xiàn)功能的。而單片機(jī)編程最合適的編程
    的頭像 發(fā)表于 11-21 10:06 ?1313次閱讀
    什么是<b class='flag-5'>C</b><b class='flag-5'>語言</b>?<b class='flag-5'>單片機(jī)</b>有什么特點(diǎn)?為什么要用<b class='flag-5'>C</b><b class='flag-5'>語言</b>編程?

    C語言單片機(jī)開發(fā)的應(yīng)用

    C語言單片機(jī)開發(fā)的應(yīng)用 單片機(jī)的開發(fā)應(yīng)用
    發(fā)表于 04-07 13:59 ?1124次閱讀

    單片機(jī)C語言教程-C語言教程附錄

    單片機(jī)C語言教程-C語言教程附錄 附錄一 C51
    發(fā)表于 01-07 15:10 ?1735次閱讀

    單片機(jī)C語言編程“位”的保存方案

    引言   現(xiàn)有的教課書及相關(guān)文章,都難得提到單片機(jī)C語言編程
    發(fā)表于 07-06 11:44 ?2225次閱讀

    AVR單片機(jī)C語言總綱

    提出了一種學(xué)習(xí)AVR單片機(jī)c語言編程的方法,并提供了完整的教程AVR單片機(jī)c語言總綱.
    發(fā)表于 04-13 14:59 ?154次下載

    單片機(jī)C語言輕松入門

    單片機(jī)c語言的融合,供那些不知道怎么將c語言單片機(jī)融合的人學(xué)習(xí)和借鑒
    發(fā)表于 12-21 15:11 ?26次下載

    單片機(jī)C語言編程與實(shí)例

    單片機(jī)C語言編程與實(shí)例 學(xué)習(xí)單片機(jī)開發(fā)非常不錯的資料。
    發(fā)表于 01-11 14:50 ?41次下載

    單片機(jī)c語言教程

    電子專業(yè)單片機(jī)相關(guān)知識學(xué)習(xí)教材資料-單片機(jī)c語言教程
    發(fā)表于 09-01 15:44 ?0次下載

    8051單片機(jī)C語言軟件設(shè)計(jì)8051單片機(jī)C語言軟件設(shè)計(jì)

    8051單片機(jī)C語言軟件設(shè)計(jì)8051單片機(jī)C語言軟件設(shè)計(jì)
    發(fā)表于 10-16 11:25 ?92次下載
    8051<b class='flag-5'>單片機(jī)</b><b class='flag-5'>C</b><b class='flag-5'>語言</b>軟件設(shè)計(jì)8051<b class='flag-5'>單片機(jī)</b><b class='flag-5'>C</b><b class='flag-5'>語言</b>軟件設(shè)計(jì)

    學(xué)習(xí)單片機(jī)一定要先學(xué)好C語言再去學(xué)單片機(jī)

    首先肯定一點(diǎn)的是學(xué)習(xí)單片機(jī)不需要先學(xué)好C語言再去學(xué)單片機(jī),而是在學(xué)習(xí)單片機(jī)的過程可以促進(jìn)
    的頭像 發(fā)表于 01-26 15:30 ?1.4w次閱讀

    單片機(jī)C語言程序設(shè)計(jì)的詳細(xì)資料

    編譯器(簡稱C51),轉(zhuǎn)換生成單片機(jī)執(zhí)行的代碼程序。 基于51系列單片機(jī)C語言
    發(fā)表于 07-07 14:48 ?75次下載
    <b class='flag-5'>單片機(jī)</b><b class='flag-5'>C</b><b class='flag-5'>語言</b>程序設(shè)計(jì)的詳細(xì)資料

    單片機(jī)C語言C語言為什么有差異?

    許多小伙伴在學(xué)完C語言后想入門單片機(jī),但學(xué)著學(xué)著發(fā)現(xiàn)明明都是C語言,為什么單片機(jī)
    發(fā)表于 09-01 16:39 ?3671次閱讀

    1.單片機(jī)C語言的關(guān)系(5)

    單片機(jī)C語言的關(guān)系1.單片機(jī)一般使用C語言來編程2.學(xué)習(xí)
    發(fā)表于 11-10 20:35 ?18次下載
    1.<b class='flag-5'>單片機(jī)</b>和<b class='flag-5'>C</b><b class='flag-5'>語言</b>的關(guān)系(5)

    單片機(jī)c語言入門指南

    隨著單片機(jī)開發(fā)技術(shù)的不斷發(fā)展,目前已有越來越多的人從普遍使用匯編語言到逐漸使 用高級語言開發(fā),其中主要是以 C 語言為主,市場上幾種常見的
    發(fā)表于 07-07 14:34 ?6次下載