chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

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

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

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

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

科技綠洲 ? 來(lái)源:網(wǎng)絡(luò)整理 ? 作者:網(wǎng)絡(luò)整理 ? 2023-11-24 15:45 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

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

Part1CPU與外設(shè)

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

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

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

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

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

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

圖片細(xì)看這幾個(gè)步驟,有些疑問(wèn),a的值讀出來(lái)后保存在CPU里面哪里?b的值讀出來(lái)后保存在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é)果再寫(xiě)回到內(nèi)存中,執(zhí)行第四步匯編代碼STR R0,[a],意思是將R0中的值寫(xiě)入到變量a的地址處0x12。

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

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

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

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

1.1 Flash

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

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

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

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

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

1.2 SRAM(內(nèi)存)

1.2.1 棧

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

圖片

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

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

圖片

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

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

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

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

此時(shí)向棧中壓入了三個(gè)個(gè)數(shù)據(jù),每個(gè)數(shù)據(jù)都是4字節(jié)的,所以SP向下移動(dòng)了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中的值,寫(xiě)入到sp + 0x04地址處。

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

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

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

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

圖片

如上圖所示,創(chuàng)建100字節(jié)大小的數(shù)組,先開(kāi)辟100個(gè)字節(jié)大小的??臻g,執(zhí)行匯編語(yǔ)句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é)大小的棧用來(lái)存放這個(gè)str數(shù)組,此時(shí)它不使用占坑的方式了,而是直接改變SP的值來(lái)改變棧區(qū)的大小。

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

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

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

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

圖片

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

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

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

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

1.2.3 堆

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

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

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

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

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

圖片

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

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

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

圖片

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

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

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

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

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

Part2變量的初始化

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

2.1 局部變量

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

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

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

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

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

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

對(duì)于初始值為0x11223344的初始化,兩個(gè)字節(jié)的指令無(wú)法容納四個(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ù)來(lái)初始化這個(gè)數(shù)組,這個(gè)函數(shù)是由編譯器生成的,也是一堆匯編語(yǔ)句,這里暫不做介紹。

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

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

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

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

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

圖片

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

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

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

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

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

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

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

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

Part3函數(shù)

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

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

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

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

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

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

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

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

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

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

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

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

Part4指針變量

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

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

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

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

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

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

Part5結(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從初值起始地址開(kāi)始讀取初值0x0000 18,0x0000 00001,0x0000 0064,對(duì)應(yīng)著24,1,100。

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

通過(guò)調(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)體的訪問(wèn)效率,結(jié)構(gòu)體變量在存放時(shí)會(huì)進(jìn)行內(nèi)存對(duì)齊。

圖片

如上圖,數(shù)據(jù)線和地址線都是32位的,也就是4字節(jié),除此之外還有四根控制線be0,be1,be2,be3。無(wú)論是訪問(wèn)還是寫(xiě)入,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)存對(duì)齊方案,雖然char sex浪費(fèi)了三個(gè)字節(jié)的空間,但是在操作int score的時(shí)候,可以一次性操作完畢,不需要第二次。

  • 結(jié)構(gòu)體對(duì)齊利用了以空間換時(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 weight,char kgint g兩種類型的變量共用這一個(gè)空間。而且可以看到,weightkg,g三者的地址都是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é)全部改變。

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

Part6總結(jié)

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

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

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

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

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

    關(guān)注

    6067

    文章

    44992

    瀏覽量

    650676
  • WINDOWS
    +關(guān)注

    關(guān)注

    4

    文章

    3614

    瀏覽量

    91438
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7632

    瀏覽量

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

    關(guān)注

    117

    文章

    3826

    瀏覽量

    83013
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    8051單片機(jī)C語(yǔ)言軟件設(shè)計(jì)8051單片機(jī)C語(yǔ)言軟件設(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'>語(yǔ)言</b>軟件設(shè)計(jì)8051<b class='flag-5'>單片機(jī)</b><b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>軟件設(shè)計(jì)

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

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

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

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

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

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

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

    單片機(jī)C語(yǔ)言的關(guān)系1.單片機(jī)一般使用C語(yǔ)言來(lái)編程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'>語(yǔ)言</b>的關(guān)系(5)

    C語(yǔ)言條件編譯語(yǔ)句and單片機(jī)DMA的介紹

    C語(yǔ)言條件編譯語(yǔ)句and單片機(jī)DMA的介紹C語(yǔ)言條件編譯:這里面介紹的很詳細(xì),也有歷程。DMA的介紹:介紹了
    發(fā)表于 11-29 10:36 ?3次下載
    <b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>條件編譯語(yǔ)句and<b class='flag-5'>單片機(jī)</b>DMA的介紹

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

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