溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題

發(fā)布時(shí)間:2022-03-04 10:31:46 來(lái)源:億速云 閱讀:157 作者:小新 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

    一、內(nèi)存分區(qū)概念介紹

    1.1、C/C++編譯程序的內(nèi)存占用

    1、棧區(qū)(stack)
    由編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
    2、堆區(qū)(heap)
    一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。它與數(shù)據(jù)結(jié)構(gòu)中的堆不同,分配方式類似于鏈表。
    3、全局區(qū)(靜態(tài)區(qū))(static)
    全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量、未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。當(dāng)程序結(jié)束后,變量由系統(tǒng)釋放 。
    4、文字常量區(qū)
    存放常量字符串。當(dāng)程序結(jié)束后,常量字符串由系統(tǒng)釋放 。
    5、程序代碼區(qū)
    存放函數(shù)體的二進(jìn)制代碼。

    1、從靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。例如全局變量,static變量。

    2、在棧上創(chuàng)建。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。
    3、從堆上分配,亦稱動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用malloc或new申請(qǐng)任意多少的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用free或delete釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由程序員決定,使用非常靈活,但如果在堆上分配了空間,就有責(zé)任回收它,否則運(yùn)行的程序會(huì)出現(xiàn)內(nèi)存泄漏,頻繁地分配和釋放不同大小的堆空間將會(huì)產(chǎn)生堆內(nèi)碎塊。

    1.2、棧和堆、全局/靜態(tài)存儲(chǔ)區(qū)和常量存儲(chǔ)區(qū)的對(duì)比

    如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題

    1、棧區(qū): 主要用來(lái)存放局部變量, 傳遞參數(shù), 存放函數(shù)的返回地址。.esp 始終指向棧頂, 棧中的數(shù)據(jù)越多, esp的值越小。

    2、堆區(qū): 用于存放動(dòng)態(tài)分配的對(duì)象, 當(dāng)你使用 malloc和new 等進(jìn)行分配時(shí),所得到的空間就在堆中。動(dòng)態(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)存地址生長(zhǎng); 棧向低內(nèi)存地址生長(zhǎng); 堆和棧相向而生,堆和棧之間有個(gè)臨界點(diǎn),稱為stkbrk。

    1.3、圖片說(shuō)明

    如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題 

    補(bǔ)充:

    Stack: 棧,存放Automatic Variables,按內(nèi)存地址由高到低方向生長(zhǎng),其最大大小由編譯時(shí)確定,速度快,但自由性差,最大空間不大。
    Heap: 堆,自由申請(qǐng)的空間,按內(nèi)存地址由低到高方向生長(zhǎng),其大小由系統(tǒng)內(nèi)存/虛擬內(nèi)存上限決定,速度較慢,但自由性大,可用空間大。
    每個(gè)線程都會(huì)有自己的棧,但是堆空間是共用的。

    參考文獻(xiàn):https://www.icode9.com/content-1-772915.html 

     二、C語(yǔ)言編程論證

    1.1、Ubuntu測(cè)試代碼實(shí)現(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";
        //動(dòng)態(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ū)-動(dòng)態(tài)申請(qǐng)地址\n");
        printf("                   %p\n", p1);
        printf("                   %p\n", p2);
        printf("\n全局區(qū)-全局變量和靜態(tài)變量\n");
        printf("\n.bss段\n");
        printf("全局外部無(wú)初值 uninit_global_a:%p\n", &uninit_global_a);
        printf("靜態(tài)外部無(wú)初值 uninits_global_b:%p\n", &uninits_global_b);
        printf("靜態(tài)內(nèi)部無(wú)初值 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)建一個(gè) main.c 文件:

    gedit main.c

    3、復(fù)制粘貼上述代碼

    4、編譯執(zhí)行

    gcc main.c -o main

    ./main

    分析說(shuō)明:可以從上圖可以得出棧區(qū)內(nèi)存地址由高到低方向生長(zhǎng),堆區(qū)內(nèi)存地址由低到高方向生長(zhǎng)。而且整個(gè)程序的內(nèi)存也是從高到低的地址進(jìn)行分配的。 

     1.2、STM32驗(yàn)證代碼實(shí)現(xiàn)

    打開(kāi)之前做過(guò)的串口通訊實(shí)驗(yàn)代碼,在其中進(jìn)行修改,后面我也會(huì)給出先關(guān)串口鏈接

    1、代碼更改

    修改bsp_usart.h :

    添加頭文件:

    #include <stdio.h>
    #include <stdlib.h>

    如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題

     修改bsp_usart.c :

    重寫(xiě) 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);
    }

    如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題

    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";
    	//動(dòng)態(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ū)-動(dòng)態(tài)申請(qǐng)地址\n");
    	printf("                   %p\n", p1);
    	printf("                   %p\n", p2);
    	printf("\n全局區(qū)-全局變量和靜態(tài)變量\n");
    	printf("\n.bss段\n");
    	printf("全局外部無(wú)初值 uninit_global_a:%p\n", &uninit_global_a);
    	printf("靜態(tài)外部無(wú)初值 uninits_global_b:%p\n", &uninits_global_b);
    	printf("靜態(tài)內(nèi)部無(wú)初值 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、燒錄

    打開(kāi)串口調(diào)試助手,打開(kāi)串口后,按一下 RESET 鍵,顯示結(jié)果:

    如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題

     4、分析說(shuō)明

       與Ubuntu一樣,stm32的棧區(qū)的地址值是從上到下減小的,堆區(qū)則是從上到下增長(zhǎng)的。從每個(gè)區(qū)來(lái)看,地址值是從上到下逐步減小的,即棧區(qū)的地址是高地址,代碼區(qū)的地址是處于低地址。

    1.3、keil下stm32存儲(chǔ)觀察

    stm32數(shù)據(jù)的存儲(chǔ)位置

    1、RAM(隨機(jī)存取存儲(chǔ)器)
    存儲(chǔ)的內(nèi)容可通過(guò)指令隨機(jī)讀寫(xiě)訪問(wèn)。RAM中的存儲(chǔ)的數(shù)據(jù)在掉電是會(huì)丟失,因而只能在開(kāi)機(jī)運(yùn)行時(shí)存儲(chǔ)數(shù)據(jù)。其中RAM又可以分為兩種,一種是Dynamic RAM(DRAM動(dòng)態(tài)隨機(jī)存儲(chǔ)器),另一種是Static RAM(SRAM,靜態(tài)隨機(jī)存儲(chǔ)器)。棧、堆、全局區(qū)(.bss段、.data段)都是存放在RAM中。
    2、ROM(只讀存儲(chǔ)器)
    只能從里面讀出數(shù)據(jù)而不能任意寫(xiě)入數(shù)據(jù)。ROM與RAM相比,具有讀寫(xiě)速度慢的缺點(diǎn)。但由于其具有掉電后數(shù)據(jù)可保持不變的優(yōu)點(diǎn),因此常用也存放一次性寫(xiě)入的程序和數(shù)據(jù),比如主版的BIOS程序的芯片就是ROM存儲(chǔ)器。代碼區(qū)和常量區(qū)的內(nèi)容是不允許被修改的,所以存放于ROM中。

    查看:

    如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題

    分析說(shuō)明:

        從圖片中可以看出ROM的地址分配是從0x8000000開(kāi)始,整個(gè)大小為0x40000,這個(gè)部分用于存放代碼區(qū)和文字常量區(qū)。RAM的地址分配是從0x20000000開(kāi)始,其大小是0xC000,這個(gè)區(qū)域用來(lái)存放棧、堆、全局區(qū)(.bss段、.data段)。與代碼結(jié)果顯示進(jìn)行對(duì)比,也可以看出對(duì)應(yīng)得部分得地址與設(shè)置的是相對(duì)應(yīng)的。 

    感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“如何解決C程序中Ubuntu、stm32的內(nèi)存分配問(wèn)題”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI