參考文章https://www.cnblogs.com/yif1991/p/5049638.html
存儲時結構
首先寫一個c語言版的Hello World
#include <stdio.h>
void main()
{
printf("hello,world\n");
}
將該段程序寫在一個hello.c的文件中。打開終端,編譯hello.c生成一個a.out的文件。
終端上展示的是一個c語言的可執行文件在不同的內存所佔的空間,text、data、bss分別代表內存中不同的區域,dec代表十進制總和,hex代表着16進制總和,filename代表着文件名。
text 代碼區。
程序被操作系統加載到內存的時候,所有的可執行代碼(程序代碼指令、常量字符串等)都加載到代碼區,這塊內存在程序運行期間是不變的。代碼區是平行的,裏面裝的就是一堆指令,在程序運行期間是不能改變的。函數也是代碼的一部分,故函數都被放在代碼區,包括main函數。
注意:“int a = 0;”語句可拆分成”int a;”和”a = 0”,定義變量a的”int a;”語句並不是代碼,它在程序編譯時就執行了,並沒有放到代碼區,放到代碼區的只有”a = 0”這句。
靜態區
- 該區包含了在程序中明確被初始化的全局變量、靜態變量(包括全局靜態變量和局部靜態變量)和常量數據(如字符串常量)
int x = 99;
static x = 100;
未初始化數據區
- BSS區(uninitialized data segment)
- BSS區的數據在程序開始執行之前被內核初始化爲0或者空指針(NULL)
int x;
以上爲可執行代碼存儲時結構。
運行時結構
運行時結構多個棧區和堆區。
棧(stack)
- 一種先進後出的內存結構,局部變量(自動變量)和函數形式參數都存儲在此,存儲的這個動作由編譯器自動完成,寫程序時不需要考慮。
- 棧區在程序運行期間是可以隨時修改的。當一個自動變量超出其作用域時,自動從棧中彈出。
- 每個線程都有自己專屬的棧。
- 棧的最大尺寸固定,超出則引起棧溢出。
- 變量離開作用域後棧上的內存會自動釋放。
下面這個例子可以看出地址分配規律。
#include <stdio.h>
int n = 0;
void test(int a, int b);
int main() {
static int m = 0;
int a = 0;
int b = 0;
printf("自動變量a的地址是:%d\n自動變量b的地址是:%d\n", &a, &b);
printf("全局變量n的地址是:%d\n靜態變量m的地址是:%d\n", &n, &m);
test(a, b);
printf("main函數的地址是:%d", &main);
}
void test(int x, int y)
{
printf("形式參數x的地址是:%d\n形式參數y的地址是:%d\n",&x, &y);
}
運行結果如下:
- 由運行結果可以知道局部變量和形參地址相似,存儲在一個內存區;全局變量和靜態變量地址相似,存儲在一個區。根據運行結果中a,b,x,y的地址值大小可以還原佔內存的內存分配原理。局部變量按照執行順序入棧,函數參數的入棧順序是從右到左。
內存與指針
#include <stdio.h>
int *getx()
{
int x = 10;
return &x;
}
int main()
{
int *p = getx();
printf("%d\n", *getx());
printf("%d\n", *p);
*p = 20;
printf("%d\n", *p);
}
這段代碼沒有任何語法錯誤,編譯也可以正常通過。因爲int *p = getx()中變量x的作用域爲getx()函數體內部,這裏得到一個臨時棧變量x的地址,getx()函數調用結束後這個地址就無效了。所以有關於這個地址的任何操作都不一定成立了。
- 棧不會很大,一般都是以K爲單位。如果在程序中直接將較大的數組保存在函數內的棧變量中,很可能會內存溢出,導致程序崩潰。比較大的內存就要用到堆(heap)內存了。
堆(heap)
- 堆是一種在程序運行過程中可以隨時修改的內存區域,但沒有棧那樣先進後出的順序。更重要的是堆是一個大容器,它的容量要遠遠大於棧,這可以解決上面實驗三造成的內存溢出困難。一般比較複雜的數據類型都是放在堆中。
- 在C語言中,堆內存空間的申請和釋放需要手動通過代碼來完成。對於一個32位操作系統,最大管理管理4G內存,其中1G是給操作系統自己用的,剩下的3G都是給用戶程序,一個用戶程序理論上可以使用3G的內存空間。堆上的內存必須手動釋放(C/C++)
malloc與free
- malloc函數用來在堆中分配指定大小的內存,單位爲字節(Byte),函數返回void *指針。
- free負責在堆中釋放malloc分配的內存。malloc與free一定成對使用。
#include <stdio.h>
#include "stdlib.h"
#include "string.h"
void print_array(char *p, char n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("p[%d] = %d\n", i, p[i]);
}
}
int main()
{
char *p = (char *)malloc(1024*1024*1024);//在堆中申請了內存
memset(p, 'a', 10);//初始化內存
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i + 65;
}
print_array(p, 10);
free(p);//釋放申請的堆內存
}
運行結果如下:
改造佔內存中那個以指針作爲返回值的getx()的例子,改爲申請堆內存,即可完成需求,但一定用通過free函數釋放申請的堆內存空間。改造以後的函數如下:
#include <stdio.h>
#include "stdlib.h"
#include "string.h"
int *getx()
{
int *p = (int *)malloc(sizeof(int));//申請了一個堆空間
return p;
}
int main()
{
int *pp = getx();
*pp = 10;
printf("%d\n", *pp);
free(pp);
}
用來在堆中申請內存空間的函數還有calloc和realloc,用法與malloc類似。文章地址:https://www.cnblogs.com/lidabo/p/4611411.html
動態創建數組,則用堆。
#include <stdio.h>
#include "stdlib.h"
int main() {
int len;
int * arr;
printf("請輸入數組長度:");
scanf("%d", &len);
arr = (int *)malloc(sizeof(int)*len);
printf("請輸入數組的值:");
for ( int i = 0; i < len; i ++) {
scanf("%d", &arr[i]);
}
for (int j = 0; j < len; j ++) {
printf("%d:%d ", j , arr[j]);
}
free(arr);
return 0;
}