C語言的內存分配


01、ANSI C

在ANSI C中數據類型包括:整形,浮點型,指針和聚合型(如數組和結構等)

圖片

整形:

字符,短整型,整型和長整型,他們都分別有有符號(singed)和無符號(unsingned)

取值范圍:

沒有帶signed或者unsigned,默認signed

圖片

長整型至少應該和整型一樣長,而整型至少應該和短整型一樣長

在32位環境中,各種數據類型的長度一般如下:

圖片

02、ARM C

具體我們以IAR為編譯器,版本7.2

注意:

在32位ARM中,字是32位,半字是16位,字節是8位

圖片

可以看到以下關於整型的數據類型

圖片

下面使用typedef重新定義數據類型,沒有使用到long,因為都是32位的有一個int就夠了

typedef unsigned  char       uint8;    //!< 無符號8位整型變量 
typedef signed    char       int8;     //!< 有符號8位整型變量  
typedef unsigned  short      uint16;   //!<無符號16位整型變量 
typedef signed    short      int16;    //!< 有符號16位整型變量 
typedef unsigned  int        uint32;   //!< 無符號32位整型變量 
typedef signed    int        int32;    //!<有符號32位整型變量 
typedef float                fp32;     //!< 單精度浮點數(32位長度) 
typedef double               fp64;     //!< 雙精度浮點數(64位長度)

03、C語言內存分配方法

在標准C語言中,編譯出來的可執行程序分為代碼區(text)、數據區(data)和未初始化數據區(bss)3個部分。如下代碼

#include <stdlib.h>
int a = 0;    //a在全局已初始化數據區 
char *p1;    //p1在BSS區(未初始化全局變量) 
void main() 
{
    int b; //b在棧區
    int c; //C為全局(靜態)數據,存在於已初始化數據區
    char s[] = "abc"; //s為數組變量,存儲在棧區,
    char *p2,*p3;  //p2、p3在棧區
    p2 = (char *)malloc(10);//分配得來的10個字節的區域在堆區
    p3 = (char *)malloc(20);//分配得來的20個字節的區域在堆區
    free(p2);
    free(p3);
}

使用linux編譯之后得到的可執行文件如下

圖片

可以看到代碼區(text)、數據區(data)和未初始化數據區(bss)。

代碼段(text):存放代碼的地方。只能訪問,不能修改,代碼段就是程序中的可執行部分,直觀理解代碼段就是函數堆疊組成的。

數據段(data):全局變量和靜態局部變量存放的地方。也被稱為數據區、靜態數據區、靜態區:數據段就是程序中的數據,直觀理解就是C語言程序中的全局變量。注意是全局變量或靜態局部變量,局部變量不算。

未初始化數據區(bss):bss段的特點就是被初始化為0,bss段本質上也是屬於數據段。

那么問題來了,為什么要區分data段和bss段呢?

以下面代碼為例,a.c和b.c的差異只是有沒有給arr數組賦值。

圖片

可以看到a.out的bss段大,b.out的data段大。但是b.out的文件大小明顯比a.out的大很多。

圖片

那么就可以簡單理解為,data段會增大可執行文件的大小,而bss段不會。

這里我說下自己的理解,我並沒有找到資料驗證:

data段是全局變量,但是需要初始化值,上面我的例子是全部初始全部為1,但也可能是1024*1024個不同的數據,而這些數據需要保存起來,表現出來也就是需要保存在可執行文件中。

bss段也是全局變量,但不需要初始化值,只需要保存一下這個全部變量的保存的數據類型和大小即可。即使它的數組容量是1024*1024,也不會占用很多可執行文件的大小。

 

這里再說明一個問題:如果一個全部變量初始化為0,那么它也是bss段,不是data段,即使你代碼中把它初始化為0了。這點大家可以自行驗證。

 

關於數據段,也就是data段,也會分為RO data(只讀數據段)和RW data(讀寫數據段)。

從字面意思就可以區分他們的意思,不同的是:

只讀數據段:程序使用的一些不會被更改的數據,使用這些數據的方式類似查表式的操作,由於這些變量不需要更改,因此只需要放置在只讀存儲器中即可。

讀寫數據段:程序中是可以被更改的數據,且初始化過的,所以需要放置在RAM中,且初始化的內容放在存儲器中(表現為放入可執行文件中)。

這樣又可以分區只讀區和讀寫區域,如下所所示(當然bss段和下文的堆棧也是讀寫區)

圖片

上面說到“編譯出來的可執行程序分為代碼區(text)、數據區(data)和未初始化數據區(bss)3個部分”,那運行中就會多出來一些區域,這就是我們常說的堆棧,注意堆棧是兩個區域堆和棧。

棧:局部變量、函數一般在棧空間中。運行時自動分配&自動回收:棧是自動管理的,程序員不需要手工干預。方便簡單。是提前分配好的連續的地址空間。棧的增長方向是向下的,即向着內存地址減小的方向。

堆:堆內存管理者總量很大的操作系統內存塊,各進程可以按需申請使用,使用完釋放。程序手動申請&釋放:手工意思是需要寫代碼去申請malloc和釋放free。可以是不連續的地址空間。堆的增長方向是向上的,即向着內存地址增加的方向。

圖片

下面是簡單的演示代碼

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

int bss_var;                                //未初始化全局數據存儲在BSS區
int data_var=42;                            //初始化全局數據存儲在數據區

int main(int argc,char *argv[])
{
  char *p ,*b;
  printf("Adr bss_var:0x%x\n",&bss_var);
  printf("Adr data_var:0x%x\n",&data_var);
  printf("the %s is at adr:0x%x\n","main",&main);
  p=(char *)alloca(32);              //從棧中分配空間
  if(p!=NULL)
  {
    printf("the p start is at adr:0x%x\n",p);
    printf("the p end is at adr:0x%x\n",p+31);
  }
  b=(char *)malloc(32*sizeof(char));   //從堆中分配空間
  if(b!=NULL)
  {
    printf("the b start is at adr:0x%x\n",b);
    printf("the b end is at adr:0x%x\n",b+31);
  }
  free(b);         //釋放申請的空間,以避免內存泄漏
  while(1);
}

 運行結果如下

圖片

內存分配示意圖如下

圖片

 點擊查看本文所在的專輯,STM32F207教程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM