C程序中Ubuntu、stm32的內(nèi)存分配問題
一、內(nèi)存分區(qū)概念介紹
1.1、C/C++編譯程序的內(nèi)存占用
1、棧區(qū)(stack)
由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
2、堆區(qū)(heap)
一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由OS回收 。它與數(shù)據(jù)結(jié)構(gòu)中的堆不同,分配方式類似于鏈表。
3、全局區(qū)(靜態(tài)區(qū))(static)
全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量、未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。當程序結(jié)束后,變量由系統(tǒng)釋放 。
4、文字常量區(qū)
存放常量字符串。當程序結(jié)束后,常量字符串由系統(tǒng)釋放 。
5、程序代碼區(qū)
存放函數(shù)體的二進制代碼。
1、從靜態(tài)存儲區(qū)域分配。內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。例如全局變量,static變量。
2、在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。
3、從堆上分配,亦稱動態(tài)內(nèi)存分配。程序在運行的時候用malloc或new申請任意多少的內(nèi)存,程序員自己負責在何時用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期由程序員決定,使用非常靈活,但如果在堆上分配了空間,就有責任回收它,否則運行的程序會出現(xiàn)內(nèi)存泄漏,頻繁地分配和釋放不同大小的堆空間將會產(chǎn)生堆內(nèi)碎塊。
1.2、棧和堆、全局/靜態(tài)存儲區(qū)和常量存儲區(qū)的對比
1、棧區(qū): 主要用來存放局部變量, 傳遞參數(shù), 存放函數(shù)的返回地址。.esp 始終指向棧頂, 棧中的數(shù)據(jù)越多, esp的值越小。
2、堆區(qū): 用于存放動態(tài)分配的對象, 當你使用 malloc和new 等進行分配時,所得到的空間就在堆中。動態(tài)分配得到的內(nèi)存區(qū)域附帶有分配信息, 所以你能夠 free和delete它們。
3、數(shù)據(jù)區(qū): 全局,靜態(tài)和常量是分配在數(shù)據(jù)區(qū)中的,數(shù)據(jù)區(qū)包括bss(未初始化數(shù)據(jù)區(qū))和初始化數(shù)據(jù)區(qū)。 ?
注意: 堆向高內(nèi)存地址生長; 棧向低內(nèi)存地址生長; 堆和棧相向而生,堆和棧之間有個臨界點,稱為stkbrk。
1.3、圖片說明
?
補充:
Stack: 棧,存放Automatic Variables,按內(nèi)存地址由高到低方向生長,其最大大小由編譯時確定,速度快,但自由性差,最大空間不大。
Heap: 堆,自由申請的空間,按內(nèi)存地址由低到高方向生長,其大小由系統(tǒng)內(nèi)存/虛擬內(nèi)存上限決定,速度較慢,但自由性大,可用空間大。
每個線程都會有自己的棧,但是堆空間是共用的。
參考文獻:https://www.icode9.com/content-1-772915.html?
?二、C語言編程論證
1.1、Ubuntu測試代碼實現(xiàn)
1、main.c:
#include <stdio.h> #include <stdlib.h> //定義全局變量 int init_global_a = 1; int uninit_global_a; static int inits_global_b = 2; static int uninits_global_b; void output(int a) { printf("hello"); printf("%d",a); printf("\n"); } int main( ) { //定義局部變量 int a=2; static int inits_local_c=2, uninits_local_c; int init_local_d = 1; output(a); char *p; char str[10] = "lyy"; //定義常量字符串 char *var1 = "1234567890"; char *var2 = "qwertyuiop"; //動態(tài)分配 int *p1=malloc(4); int *p2=malloc(4); //釋放 free(p1); free(p2); printf("棧區(qū)-變量地址\n"); printf(" a:%p\n", &a); printf(" init_local_d:%p\n", &init_local_d); printf(" p:%p\n", &p); printf(" str:%p\n", str); printf("\n堆區(qū)-動態(tài)申請地址\n"); printf(" %p\n", p1); printf(" %p\n", p2); printf("\n全局區(qū)-全局變量和靜態(tài)變量\n"); printf("\n.bss段\n"); printf("全局外部無初值 uninit_global_a:%p\n", &uninit_global_a); printf("靜態(tài)外部無初值 uninits_global_b:%p\n", &uninits_global_b); printf("靜態(tài)內(nèi)部無初值 uninits_local_c:%p\n", &uninits_local_c); printf("\n.data段\n"); printf("全局外部有初值 init_global_a:%p\n", &init_global_a); printf("靜態(tài)外部有初值 inits_global_b:%p\n", &inits_global_b); printf("靜態(tài)內(nèi)部有初值 inits_local_c:%p\n", &inits_local_c); printf("\n文字常量區(qū)\n"); printf("文字常量地址 :%p\n",var1); printf("文字常量地址 :%p\n",var2); printf("\n代碼區(qū)\n"); printf("程序區(qū)地址 :%p\n",&main); printf("函數(shù)地址 :%p\n",&output); return 0; }
2、使用命令創(chuàng)建一個 main.c 文件:
gedit main.c
3、復制粘貼上述代碼
4、編譯執(zhí)行
gcc main.c -o main
./main
5、結(jié)果展示
?
分析說明:可以從上圖可以得出棧區(qū)內(nèi)存地址由高到低方向生長,堆區(qū)內(nèi)存地址由低到高方向生長。而且整個程序的內(nèi)存也是從高到低的地址進行分配的。?
?1.2、STM32驗證代碼實現(xiàn)
打開之前做過的串口通訊實驗代碼,在其中進行修改,后面我也會給出先關(guān)串口鏈接
1、代碼更改
修改bsp_usart.h :
添加頭文件:
#include <stdio.h> #include <stdlib.h>
?修改bsp_usart.c :
重寫 fputc 函數(shù):
int fputc(int ch, FILE *f) { USART_SendData(DEBUG_USARTx, (uint8_t)ch); while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); return (ch); }
main.c?:?
#include "stm32f10x.h" #include "bsp_usart.h" //添加 bsp_usart.h 頭文件 int init_global_a = 1; int uninit_global_a; static int inits_global_b = 2; static int uninits_global_b; void output(int a) { printf("hello"); printf("%d",a); printf("\n"); } int main(void) { //定義局部變量 int a=2; static int inits_local_c=2, uninits_local_c; int init_local_d = 1; char *p; char str[10] = "lyy"; //定義常量字符串 char *var1 = "1234567890"; char *var2 = "qwertyuiop"; //動態(tài)分配 int *p1=malloc(4); int *p2=malloc(4); USART_Config();//串口初始化 output(a); //釋放 free(p1); free(p2); printf("棧區(qū)-變量地址\n"); printf(" a:%p\n", &a); printf(" init_local_d:%p\n", &init_local_d); printf(" p:%p\n", &p); printf(" str:%p\n", str); printf("\n堆區(qū)-動態(tài)申請地址\n"); printf(" %p\n", p1); printf(" %p\n", p2); printf("\n全局區(qū)-全局變量和靜態(tài)變量\n"); printf("\n.bss段\n"); printf("全局外部無初值 uninit_global_a:%p\n", &uninit_global_a); printf("靜態(tài)外部無初值 uninits_global_b:%p\n", &uninits_global_b); printf("靜態(tài)內(nèi)部無初值 uninits_local_c:%p\n", &uninits_local_c); printf("\n.data段\n"); printf("全局外部有初值 init_global_a:%p\n", &init_global_a); printf("靜態(tài)外部有初值 inits_global_b:%p\n", &inits_global_b); printf("靜態(tài)內(nèi)部有初值 inits_local_c:%p\n", &inits_local_c); printf("\n文字常量區(qū)\n"); printf("文字常量地址 :%p\n",var1); printf("文字常量地址 :%p\n",var2); printf("\n代碼區(qū)\n"); printf("程序區(qū)地址 :%p\n",&main); printf("函數(shù)地址 :%p\n",&output); return 0; }
2、編譯輸出
3、燒錄
打開串口調(diào)試助手,打開串口后,按一下 RESET 鍵,顯示結(jié)果:
?4、分析說明
? ?與Ubuntu一樣,stm32的棧區(qū)的地址值是從上到下減小的,堆區(qū)則是從上到下增長的。從每個區(qū)來看,地址值是從上到下逐步減小的,即棧區(qū)的地址是高地址,代碼區(qū)的地址是處于低地址。
1.3、keil下stm32存儲觀察
stm32數(shù)據(jù)的存儲位置
1、RAM(隨機存取存儲器)
存儲的內(nèi)容可通過指令隨機讀寫訪問。RAM中的存儲的數(shù)據(jù)在掉電是會丟失,因而只能在開機運行時存儲數(shù)據(jù)。其中RAM又可以分為兩種,一種是Dynamic RAM(DRAM動態(tài)隨機存儲器),另一種是Static RAM(SRAM,靜態(tài)隨機存儲器)。棧、堆、全局區(qū)(.bss段、.data段)都是存放在RAM中。
2、ROM(只讀存儲器)
只能從里面讀出數(shù)據(jù)而不能任意寫入數(shù)據(jù)。ROM與RAM相比,具有讀寫速度慢的缺點。但由于其具有掉電后數(shù)據(jù)可保持不變的優(yōu)點,因此常用也存放一次性寫入的程序和數(shù)據(jù),比如主版的BIOS程序的芯片就是ROM存儲器。代碼區(qū)和常量區(qū)的內(nèi)容是不允許被修改的,所以存放于ROM中。
查看:
分析說明:
? ? 從圖片中可以看出ROM的地址分配是從0x8000000開始,整個大小為0x40000,這個部分用于存放代碼區(qū)和文字常量區(qū)。RAM的地址分配是從0x20000000開始,其大小是0xC000,這個區(qū)域用來存放棧、堆、全局區(qū)(.bss段、.data段)。與代碼結(jié)果顯示進行對比,也可以看出對應得部分得地址與設置的是相對應的。?
三、總結(jié)
? ?通過對C語言程序里全局變量、局部變量、堆、棧等概念的重溫以及在不同平臺進行編程驗證,熟悉掌握了C語言中相關(guān)概念,并對整體的內(nèi)存地址分配由高到低,以及棧區(qū)內(nèi)存地址由高到低方向生長,堆區(qū)內(nèi)存地址由低到高方向生長進行了驗證。經(jīng)過本次實驗,主要是對C程序的內(nèi)存分配有進一步的認識,知道一個C程序內(nèi)存應該包括哪些部分。其中,主要是程序段、數(shù)據(jù)段、堆棧三個部分。不同系統(tǒng)下面,區(qū)域內(nèi)的地址值變化是不相同??偟膩碚f,是對內(nèi)存的分配有了比較新的認識。
四、參考資料
https://blog.csdn.net/qq_43279579/article/details/110308101
https://blog.csdn.net/ssj925319/article/details/110727925?spm=1001.2014.3001.5501?
USART串口通信:
鏈接: https://pan.baidu.com/s/1YspOK2I3Ddm5XntKXKRnXA
提取碼: emev?
到此這篇關(guān)于C程序中Ubuntu、stm32的內(nèi)存分配問題的文章就介紹到這了,更多相關(guān)C程序Ubuntu、stm32的內(nèi)存分配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!