国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

商城開發(fā)價(jià)格服務(wù)排名優(yōu)化百度

商城開發(fā)價(jià)格服務(wù),排名優(yōu)化百度,遼寧省建設(shè)執(zhí)業(yè)繼續(xù)教育協(xié)會網(wǎng)站,做黃色網(wǎng)站的人不犯法嗎🐱作者:一只大喵咪1201 🐱專欄:《RTOS學(xué)習(xí)》 🔥格言:你只管努力,剩下的交給時(shí)間! 本喵默認(rèn)各位小伙伴都會C語言,我們平時(shí)學(xué)習(xí)C語言都是在Windows環(huán)境下學(xué)習(xí)的&#xff0…

🐱作者:一只大喵咪1201
🐱專欄:《RTOS學(xué)習(xí)》
🔥格言:你只管努力,剩下的交給時(shí)間!
圖

本喵默認(rèn)各位小伙伴都會C語言,我們平時(shí)學(xué)習(xí)C語言都是在Windows環(huán)境下學(xué)習(xí)的,對于程序執(zhí)行的底層邏輯了解的不是非常清楚,本喵在這里給大家介紹一下,C語言在單片機(jī)中是如何執(zhí)行的。

單片機(jī)中的C語言

  • 🍞CPU與外設(shè)
    • 🥞Flash
    • 🥞SRAM(內(nèi)存)
      • 數(shù)據(jù)段
  • 🍞變量的初始化
    • 🥞局部變量
    • 🥞全局變量和靜態(tài)變量
  • 🍞函數(shù)
  • 🍞指針變量
  • 🍞結(jié)構(gòu)體和聯(lián)合體
  • 🍞總結(jié)

🍞CPU與外設(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):

① 對內(nèi)存只有讀、寫指令。

② 對于數(shù)據(jù)的運(yùn)算是在CPU內(nèi)部實(shí)現(xiàn)。

③ 使用RISC指令的CPU復(fù)雜度小一點(diǎn),易于設(shè)計(jì)。

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

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

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

假設(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:相加的匯編指令,可以有三個(gè)操作數(shù)也可以有兩個(gè)操作數(shù),三個(gè)操作數(shù)則后兩個(gè)操作數(shù)相加,得的結(jié)構(gòu)均保存到第一個(gè)操作數(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í)數(shù)據(jù),掉電數(shù)據(jù)丟失。

Flash的地址范圍是0x0800 0000 ~ 0x0807 FFFFSRAM的地址范圍是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)中。

🥞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起始地址之前,固化了一些代碼,這個(gè)暫不作說明。

🥞SRAM(內(nèi)存)

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

圖

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

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

圖

如上圖所示,在main函數(shù)中創(chuàng)建兩個(gè)變量a和b,加volatile的作用是防止編譯器將這兩個(gè)變量優(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í)向棧中壓入了三個(gè)個(gè)數(shù)據(jù),每個(gè)數(shù)據(jù)都是4字節(jié)的,所以SP向下移動了12個(gè)字節(jié),這12個(gè)字節(jié)就可以看作當(dāng)前main函數(shù)的棧幀大小。

圖

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

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

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

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


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

圖

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

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

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


數(shù)據(jù)段

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

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

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

圖

如上圖所示,當(dāng)給變量a賦值時(shí),先執(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,這是一個(gè)Flash處的地址,而該地址處的值是0x0000,由于LDR一次取四個(gè)字節(jié)的數(shù)據(jù),所以要連0x0800 0016E處的值0x2000也要讀走,兩個(gè)值按照大端存儲模式復(fù)原(高地址存放高字節(jié)序),得到的值就是0x2000 0000。

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


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

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

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

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

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

圖

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

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


為什么Flash的起始地址就是0x0800 0000SRAM的起始地址就是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ā)生什么?

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

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

🍞變量的初始化

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

🥞局部變量

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

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

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

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

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

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

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

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

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

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

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

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

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

圖

如上圖所示,定義兩個(gè)全局變量a和b,初始值分別為10和20,定義一個(gè)全局靜態(tài)變量,初始值為30,定義一個(gè)局部靜態(tài)變量,初始值為40,當(dāng)程序執(zhí)行到main中時(shí),通過調(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ī)器碼中并沒有變量名這么一說。

copy函數(shù)的實(shí)現(xiàn)本喵在以后會詳細(xì)介紹。

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

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

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

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


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

🍞函數(shù)

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

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

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

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

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

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

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

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

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

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

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

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

🍞指針變量

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

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

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

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

訪問不同類型的指針,底層會有不同的策略,讓CPU以對應(yīng)的視角去操作對應(yīng)的內(nèi)存。如*pa,CPU就會認(rèn)為它現(xiàn)在訪問地址處的變量是一個(gè)int類型,而不是一個(gè)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指令集,這個(gè)1和實(shí)際地址沒有關(guān)系。
  • 該值減去1才是真正的函數(shù)起始地址,也就是0x0800 0130。

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

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

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

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

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

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

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

圖

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

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

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

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

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

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

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

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

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


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

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

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

🍞總結(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í)候,它的一切就早被安排好了,就是按照本喵前面所講述的去安排設(shè)計(jì)的,CPU只需要按照機(jī)器碼執(zhí)行即可。

  • 其中地址的分配主要是由連接器完成。
http://m.aloenet.com.cn/news/1201.html

相關(guān)文章:

  • 和先鋒影音和做的網(wǎng)站百度關(guān)鍵詞排名推廣
  • 騎行網(wǎng)站模板網(wǎng)站搭建平臺
  • wordpress 黃藍(lán) 現(xiàn)代企業(yè)教程seo推廣排名網(wǎng)站
  • 建立網(wǎng)站需要注冊公司嗎seo引擎優(yōu)化公司
  • 網(wǎng)站做哪些主題比較容易做幽默廣告軟文案例
  • 專做外貿(mào)衣服鞋網(wǎng)站有哪些網(wǎng)址搜索引擎入口
  • 還有什么網(wǎng)站可以做面包車?yán)涀鲆粋€(gè)網(wǎng)站需要多少錢大概
  • 福建網(wǎng)站建設(shè)公司交換友情鏈接的意義是什么
  • 常州建設(shè)工程監(jiān)理員掛證網(wǎng)站百度軟件開放平臺
  • 做網(wǎng)站的時(shí)候賣過假貨而出過事搜索引擎優(yōu)化是免費(fèi)的嗎
  • 重點(diǎn)項(xiàng)目建設(shè)網(wǎng)站商業(yè)策劃公司十大公司
  • 營銷型網(wǎng)站系統(tǒng)網(wǎng)絡(luò)營銷策劃方案
  • 國內(nèi)做新聞比較好的網(wǎng)站有哪些企業(yè)網(wǎng)站制作公司
  • wordpress漢語公益搜索網(wǎng)站排名優(yōu)化
  • 網(wǎng)站被降權(quán)會發(fā)生什么長春網(wǎng)站公司哪家好
  • 廊坊網(wǎng)站快速排名優(yōu)化杭州seo營銷
  • 旅游網(wǎng)站開發(fā)功能網(wǎng)絡(luò)廣告投放網(wǎng)站
  • 公安部門網(wǎng)站備案網(wǎng)站產(chǎn)品推廣
  • 政府網(wǎng)站建設(shè)工作匯報(bào)網(wǎng)頁設(shè)計(jì)和網(wǎng)站制作
  • 寧波網(wǎng)站建設(shè)免費(fèi)咨詢漯河網(wǎng)絡(luò)推廣哪家好
  • 微信微網(wǎng)站平臺seo優(yōu)化流程
  • j昆明網(wǎng)站制作公司關(guān)鍵詞搜索指數(shù)
  • 怎么靠做網(wǎng)站賺錢嗎企業(yè)宣傳方式有哪些
  • python 做網(wǎng)站開發(fā)嗎app拉新怎么做
  • 銅山區(qū)建設(shè)局局網(wǎng)站周保春安卓優(yōu)化大師舊版
  • 網(wǎng)站搜索不到公司網(wǎng)站如何建造一個(gè)網(wǎng)站
  • 網(wǎng)址你知道我的意思的免費(fèi)何鵬seo
  • 做網(wǎng)站的服務(wù)商最新軍事新聞今日最新消息
  • 誰可以做網(wǎng)站優(yōu)化排名推廣百度管理員聯(lián)系方式
  • 讓其他公司做網(wǎng)站應(yīng)注意什么問題網(wǎng)站搜索引擎優(yōu)化主要方法