天天看點

看完這篇你還能不懂C語言/C++記憶體管理?(一)

C 語言記憶體管理指對系統記憶體的配置設定、建立、使用這一系列操作。在記憶體管理中,由于是作業系統記憶體,使用不當會造成畢竟麻煩的結果。本文将從系統記憶體的配置設定、建立出發,并且使用例子來舉例說明記憶體管理不當會出現的情況及解決辦法。

一、記憶體

在計算機中,每個應用程式之間的記憶體是互相獨立的,通常情況下應用程式 A 并不能通路應用程式 B,當然一些特殊技巧可以通路,但此文并不詳細進行說明。例如在計算機中,一個視訊播放程式與一個浏覽器程式,它們的記憶體并不能通路,每個程式所擁有的記憶體是分區進行管理的。

在計算機系統中,運作程式 A 将會在記憶體中開辟程式 A 的記憶體區域 1,運作程式 B 将會在記憶體中開辟程式 B 的記憶體區域 2,記憶體區域 1 與記憶體區域 2 之間邏輯分隔。

看完這篇你還能不懂C語言/C++記憶體管理?(一)

1.1 記憶體四區

在程式 A 開辟的記憶體區域 1 會被分為幾個區域,這就是記憶體四區,記憶體四區分為棧區、堆區、資料區與代碼區。

看完這篇你還能不懂C語言/C++記憶體管理?(一)

棧區指的是存儲一些臨時變量的區域,臨時變量包括了局部變量、傳回值、參數、傳回位址等,當這些變量超出了目前作用域時将會自動彈出。該棧的最大存儲是有大小的,該值固定,超過該大小将會造成棧溢出。

堆區指的是一個比較大的記憶體空間,主要用于對動态記憶體的配置設定;在程式開發中一般是開發人員進行配置設定與釋放,若在程式結束時都未釋放,系統将會自動進行回收。

資料區指的是主要存放全局變量、常量和靜态變量的區域,資料區又可以進行劃分,分為全局區與靜态區。全局變量與靜态變量将會存放至該區域。

代碼區就比較好了解了,主要是存儲可執行代碼,該區域的屬性是隻讀的。

1.2 使用代碼證明記憶體四區的底層結構

由于棧區與堆區的底層結構比較直覺的表現,在此使用代碼隻示範這兩個概念。首先檢視代碼觀察棧區的記憶體位址配置設定情況:

#include<stdio.h>
int main()
{
 int a = 0;
 int b = 0;
 char c='0';
 printf("變量a的位址是:%d\n變量b的位址是:%d\n變量c的位址是:%d\n", &a, &b, &c);
}      

運作結果為:

看完這篇你還能不懂C語言/C++記憶體管理?(一)

我們可以觀察到變量 a 的位址是 2293324 變量 b 的位址是 2293320,由于 int 的資料大小為 4 是以兩者之間間隔為 4;再檢視變量 c,我們發現變量 c 的位址為 2293319,與變量 b 的位址 2293324 間隔 1,因為 c 的資料類型為 char,類型大小為 1。在此我們觀察發現,明明我建立變量的時候順序是 a 到 b 再到 c,為什麼它們之間的位址不是增加而是減少呢?那是因為棧區的一種資料存儲結構為先進後出,如圖:

看完這篇你還能不懂C語言/C++記憶體管理?(一)

首先棧的頂部為位址的“最小”索引,随後往下依次增大,但是由于堆棧的特殊存儲結構,我們将變量 a 先進行存儲,那麼它的一個索引位址将會是最大的,随後依次減少;第二次存儲的值是 b,該值的位址索引比 a 小,由于 int 的資料大小為 4,是以在 a 位址為 2293324 的基礎上往上減少 4 為 2293320,在存儲 c 的時候為 char,大小為 1,則位址為 2293319。由于 a、b、c 三個變量同屬于一個棧内,是以它們位址的索引是連續性的,那如果我建立一個靜态變量将會如何?在以上内容中說明了靜态變量存儲在靜态區内,我們現在就來證明一下:

#include<stdio.h>
int main()
{
 int a = 0;
 int b = 0;
 char c='0';
 static int d = 0;
 printf("變量a的位址是:%d\n變量b的位址是:%d\n變量c的位址是:%d\n", &a, &b, &c);
 printf("靜态變量d的位址是:%d\n", &d);
}      

運作結果如下:

看完這篇你還能不懂C語言/C++記憶體管理?(一)

以上代碼中建立了一個變量 d,變量 d 為靜态變量,運作代碼後從結果上得知,靜态變量 d 的位址與一般變量 a、b、c 的位址并不存在連續,他們兩個的記憶體位址是分開的。那接下來在此建一個全局變量,通過上述内容得知,全局變量與靜态變量都應該存儲在靜态區,代碼如下:

#include<stdio.h>
int e = 0;
int main()
{
 int a = 0;
 int b = 0;
 char c='0';
 static int d = 0;
 printf("變量a的位址是:%d\n變量b的位址是:%d\n變量c的位址是:%d\n", &a, &b, &c);
 printf("靜态變量d的位址是:%d\n", &d);
 printf("全局變量e的位址是:%d\n", &e);
}      
看完這篇你還能不懂C語言/C++記憶體管理?(一)

從以上運作結果中證明了上述内容的真實性,并且也得到了一個知識點,棧區、資料區都是使用棧結構對資料進行存儲。

在以上内容中還說明了一點棧的特性,就是容量具有固定大小,超過最大容量将會造成棧溢出。檢視如下代碼:

#include<stdio.h>
 
int main()
{
 char arr_char[1024*1000000];
    arr_char[0] = '0';
}

      

以上代碼定義了一個字元數組 arr_char,并且設定了大小為 1024*1000000,設定該資料是友善檢視大小;随後在數組頭部進行指派。運作結果如下:

看完這篇你還能不懂C語言/C++記憶體管理?(一)

這是程式運作出錯,原因是造成了棧的溢出。在平常開發中若需要大容量的記憶體,需要使用堆。

繼續閱讀