溫馨提示×

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

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

C語言/C++內(nèi)存管理是什么

發(fā)布時(shí)間:2021-10-25 15:27:39 來源:億速云 閱讀:106 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“C語言/C++內(nèi)存管理是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、內(nèi)存

在計(jì)算機(jī)中,每個(gè)應(yīng)用程序之間的內(nèi)存是相互獨(dú)立的,通常情況下應(yīng)用程序 A 并不能訪問應(yīng)用程序  B,當(dāng)然一些特殊技巧可以訪問,但此文并不詳細(xì)進(jìn)行說明。例如在計(jì)算機(jī)中,一個(gè)視頻播放程序與一個(gè)瀏覽器程序,它們的內(nèi)存并不能訪問,每個(gè)程序所擁有的內(nèi)存是分區(qū)進(jìn)行管理的。

在計(jì)算機(jī)系統(tǒng)中,運(yùn)行程序 A 將會(huì)在內(nèi)存中開辟程序 A 的內(nèi)存區(qū)域 1,運(yùn)行程序 B 將會(huì)在內(nèi)存中開辟程序 B 的內(nèi)存區(qū)域 2,內(nèi)存區(qū)域 1 與內(nèi)存區(qū)域  2 之間邏輯分隔。

C語言/C++內(nèi)存管理是什么

1.1 內(nèi)存四區(qū)

在程序 A 開辟的內(nèi)存區(qū)域 1 會(huì)被分為幾個(gè)區(qū)域,這就是內(nèi)存四區(qū),內(nèi)存四區(qū)分為棧區(qū)、堆區(qū)、數(shù)據(jù)區(qū)與代碼區(qū)。

C語言/C++內(nèi)存管理是什么

棧區(qū)指的是存儲(chǔ)一些臨時(shí)變量的區(qū)域,臨時(shí)變量包括了局部變量、返回值、參數(shù)、返回地址等,當(dāng)這些變量超出了當(dāng)前作用域時(shí)將會(huì)自動(dòng)彈出。該棧的最大存儲(chǔ)是有大小的,該值固定,超過該大小將會(huì)造成棧溢出。

堆區(qū)指的是一個(gè)比較大的內(nèi)存空間,主要用于對(duì)動(dòng)態(tài)內(nèi)存的分配;在程序開發(fā)中一般是開發(fā)人員進(jìn)行分配與釋放,若在程序結(jié)束時(shí)都未釋放,系統(tǒng)將會(huì)自動(dòng)進(jìn)行回收。

數(shù)據(jù)區(qū)指的是主要存放全局變量、常量和靜態(tài)變量的區(qū)域,數(shù)據(jù)區(qū)又可以進(jìn)行劃分,分為全局區(qū)與靜態(tài)區(qū)。全局變量與靜態(tài)變量將會(huì)存放至該區(qū)域。

代碼區(qū)就比較好理解了,主要是存儲(chǔ)可執(zhí)行代碼,該區(qū)域的屬性是只讀的。

1.2 使用代碼證實(shí)內(nèi)存四區(qū)的底層結(jié)構(gòu)

由于棧區(qū)與堆區(qū)的底層結(jié)構(gòu)比較直觀的表現(xiàn),在此使用代碼只演示這兩個(gè)概念。首先查看代碼觀察棧區(qū)的內(nèi)存地址分配情況:

#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);  }

運(yùn)行結(jié)果為:

C語言/C++內(nèi)存管理是什么

我們可以觀察到變量 a 的地址是 2293324 變量 b 的地址是 2293320,由于 int 的數(shù)據(jù)大小為 4 所以兩者之間間隔為 4;再查看變量  c,我們發(fā)現(xiàn)變量 c 的地址為 2293319,與變量 b 的地址 2293324 間隔 1,因?yàn)?c 的數(shù)據(jù)類型為 char,類型大小為  1。在此我們觀察發(fā)現(xiàn),明明我創(chuàng)建變量的時(shí)候順序是 a 到 b 再到  c,為什么它們之間的地址不是增加而是減少呢?那是因?yàn)闂^(qū)的一種數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)為先進(jìn)后出,如圖:

C語言/C++內(nèi)存管理是什么

首先棧的頂部為地址的“最小”索引,隨后往下依次增大,但是由于堆棧的特殊存儲(chǔ)結(jié)構(gòu),我們將變量 a  先進(jìn)行存儲(chǔ),那么它的一個(gè)索引地址將會(huì)是最大的,隨后依次減少;第二次存儲(chǔ)的值是 b,該值的地址索引比 a 小,由于 int 的數(shù)據(jù)大小為 4,所以在 a 地址為  2293324 的基礎(chǔ)上往上減少 4 為 2293320,在存儲(chǔ) c 的時(shí)候?yàn)?char,大小為 1,則地址為 2293319。由于 a、b、c  三個(gè)變量同屬于一個(gè)棧內(nèi),所以它們地址的索引是連續(xù)性的,那如果我創(chuàng)建一個(gè)靜態(tài)變量將會(huì)如何?在以上內(nèi)容中說明了靜態(tài)變量存儲(chǔ)在靜態(tài)區(qū)內(nèi),我們現(xiàn)在就來證實(shí)一下:

#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("靜態(tài)變量d的地址是:%d\n", &d);  }

運(yùn)行結(jié)果如下:

C語言/C++內(nèi)存管理是什么

以上代碼中創(chuàng)建了一個(gè)變量 d,變量 d 為靜態(tài)變量,運(yùn)行代碼后從結(jié)果上得知,靜態(tài)變量 d 的地址與一般變量 a、b、c  的地址并不存在連續(xù),他們兩個(gè)的內(nèi)存地址是分開的。那接下來在此建一個(gè)全局變量,通過上述內(nèi)容得知,全局變量與靜態(tài)變量都應(yīng)該存儲(chǔ)在靜態(tài)區(qū),代碼如下:

#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("靜態(tài)變量d的地址是:%d\n", &d);  printf("全局變量e的地址是:%d\n", &e);  }

運(yùn)行結(jié)果如下:

C語言/C++內(nèi)存管理是什么

從以上運(yùn)行結(jié)果中證實(shí)了上述內(nèi)容的真實(shí)性,并且也得到了一個(gè)知識(shí)點(diǎn),棧區(qū)、數(shù)據(jù)區(qū)都是使用棧結(jié)構(gòu)對(duì)數(shù)據(jù)進(jìn)行存儲(chǔ)。

在以上內(nèi)容中還說明了一點(diǎn)棧的特性,就是容量具有固定大小,超過最大容量將會(huì)造成棧溢出。查看如下代碼:

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

以上代碼定義了一個(gè)字符數(shù)組 arr_char,并且設(shè)置了大小為  1024*1000000,設(shè)置該數(shù)據(jù)是方便查看大小;隨后在數(shù)組頭部進(jìn)行賦值。運(yùn)行結(jié)果如下:

C語言/C++內(nèi)存管理是什么

這是程序運(yùn)行出錯(cuò),原因是造成了棧的溢出。在平常開發(fā)中若需要大容量的內(nèi)存,需要使用堆。

堆并沒有棧一樣的結(jié)構(gòu),也沒有棧一樣的先進(jìn)后出。需要人為的對(duì)內(nèi)存進(jìn)行分配使用。代碼如下:

#include<stdio.h> #include<string.h> #include <malloc.h> int main() {  char *p1 = (char *)malloc(1024*1000000);  strcpy(p1, "這里是堆區(qū)");  printf("%s\n", p1); }

以上代碼中使用了strcpy 往手動(dòng)開辟的內(nèi)存空間 p1 中傳數(shù)據(jù)“這里是堆區(qū)”,手動(dòng)開辟空間使用 malloc,傳入申請(qǐng)開辟的空間大小  1024*1000000,在棧中那么大的空間必定會(huì)造成棧溢出,而堆本身就是大容量,則不會(huì)出現(xiàn)該情況。隨后輸出開辟的內(nèi)存中內(nèi)容,運(yùn)行結(jié)果如下:

C語言/C++內(nèi)存管理是什么

在此要注意p1是表示開辟的內(nèi)存空間地址。

二、malloc 和 free

在 C 語言(不是 C++)中,malloc 和 free 是系統(tǒng)提供的函數(shù),成對(duì)使用,用于從堆中分配和釋放內(nèi)存。malloc 的全稱是 memory  allocation 譯為“動(dòng)態(tài)內(nèi)存分配”。

2.1 malloc 和 free 的使用

在開辟堆空間時(shí)我們使用的函數(shù)為 malloc,malloc 在 C 語言中是用于申請(qǐng)內(nèi)存空間,malloc 函數(shù)的原型如下:

void *malloc(size_t size);

在 malloc 函數(shù)中,size 是表示需要申請(qǐng)的內(nèi)存空間大小,申請(qǐng)成功將會(huì)返回該內(nèi)存空間的地址;申請(qǐng)失敗則會(huì)返回  NULL,并且申請(qǐng)成功也不會(huì)自動(dòng)進(jìn)行初始化。

細(xì)心的同學(xué)可能會(huì)發(fā)現(xiàn),該函數(shù)的返回值說明為 void *,在這里 void *  并不指代某一種特定的類型,而是說明該類型不確定,通過接收的指針變量從而進(jìn)行類型的轉(zhuǎn)換。在分配內(nèi)存時(shí)需要注意,即時(shí)在程序關(guān)閉時(shí)系統(tǒng)會(huì)自動(dòng)回收該手動(dòng)申請(qǐng)的內(nèi)存  ,但也要進(jìn)行手動(dòng)的釋放,保證內(nèi)存能夠在不需要時(shí)返回至堆空間,使內(nèi)存能夠合理的分配使用。

釋放空間使用 free 函數(shù),函數(shù)原型如下:

void free(void *ptr);

free 函數(shù)的返回值為 void,沒有返回值,接收的參數(shù)為使用 malloc 分配的內(nèi)存空間指針。一個(gè)完整的堆內(nèi)存申請(qǐng)與釋放的例子如下:

#include<stdio.h> #include<string.h> #include <malloc.h>  int main() {     int n, *p, i;     printf("請(qǐng)輸入一個(gè)任意長(zhǎng)度的數(shù)字來分配空間:");     scanf("%d", &n);          p = (int *)malloc(n * sizeof(int));  if(p==NULL){   printf("申請(qǐng)失敗\n");   return 0;  }else{   printf("申請(qǐng)成功\n");  }     memset(p, 0, n * sizeof(int));//填充0     //查看      for (i = 0; i < n; i++)         printf("%d ", p[i]);     printf("\n");      free(p);     p = NULL;     return 0; }

以上代碼中使用了 malloc 創(chuàng)建了一個(gè)由用戶輸入創(chuàng)建指定大小的內(nèi)存,判斷了內(nèi)存地址是否創(chuàng)建成功,且使用了 memset  函數(shù)對(duì)該內(nèi)存空間進(jìn)行了填充值,隨后使用 for 循環(huán)進(jìn)行了查看。最后使用了 free 釋放了內(nèi)存,并且將 p 賦值  NULL,這點(diǎn)需要主要,不能使指針指向未知的地址,要置于  NULL;否則在之后的開發(fā)者會(huì)誤以為是個(gè)正常的指針,就有可能再通過指針去訪問一些操作,但是在這時(shí)該指針已經(jīng)無用,指向的內(nèi)存也不知此時(shí)被如何使用,這時(shí)若出現(xiàn)意外將會(huì)造成無法預(yù)估的后果,甚至導(dǎo)致系統(tǒng)崩潰,在  malloc 的使用中更需要需要。

2.2 內(nèi)存泄漏與安全使用實(shí)例與講解

內(nèi)存泄漏是指在動(dòng)態(tài)分配的內(nèi)存中,并沒有釋放內(nèi)存或者一些原因造成了內(nèi)存無法釋放,輕度則造成系統(tǒng)的內(nèi)存資源浪費(fèi),嚴(yán)重的導(dǎo)致整個(gè)系統(tǒng)崩潰等情況的發(fā)生。

C語言/C++內(nèi)存管理是什么

內(nèi)存泄漏通常比較隱蔽,且少量的內(nèi)存泄漏發(fā)生不一定會(huì)發(fā)生無法承受的后果,但由于該錯(cuò)誤的積累將會(huì)造成整體系統(tǒng)的性能下降或系統(tǒng)崩潰。特別是在較為大型的系統(tǒng)中,如何有效的防止內(nèi)存泄漏等問題的出現(xiàn)變得尤為重要。例如一些長(zhǎng)時(shí)間的程序,若在運(yùn)行之初有少量的內(nèi)存泄漏的問題產(chǎn)生可能并未呈現(xiàn),但隨著運(yùn)行時(shí)間的增長(zhǎng)、系統(tǒng)業(yè)務(wù)處理的增加將會(huì)累積出現(xiàn)內(nèi)存泄漏這種情況;這時(shí)極大的會(huì)造成不可預(yù)知的后果,如整個(gè)系統(tǒng)的崩潰,造成的損失將會(huì)難以承受。由此防止內(nèi)存泄漏對(duì)于底層開發(fā)人員來說尤為重要。

C  程序員在開發(fā)過程中,不可避免的面對(duì)內(nèi)存操作的問題,特別是頻繁的申請(qǐng)動(dòng)態(tài)內(nèi)存時(shí)會(huì)及其容易造成內(nèi)存泄漏事故的發(fā)生。如申請(qǐng)了一塊內(nèi)存空間后,未初始化便讀其中的內(nèi)容、間接申請(qǐng)動(dòng)態(tài)內(nèi)存但并沒有進(jìn)行釋放、釋放完一塊動(dòng)態(tài)申請(qǐng)的內(nèi)存后繼續(xù)引用該內(nèi)存內(nèi)容;如上所述這種問題都是出現(xiàn)內(nèi)存泄漏的原因,往往這些原因由于過于隱蔽在測(cè)試時(shí)不一定會(huì)完全清楚,將會(huì)導(dǎo)致在項(xiàng)目上線后的長(zhǎng)時(shí)間運(yùn)行下,導(dǎo)致災(zāi)難性的后果發(fā)生。

如下是一個(gè)在子函數(shù)中進(jìn)行了內(nèi)存空間的申請(qǐng),但是并未對(duì)其進(jìn)行釋放:

#include<stdio.h> #include<string.h> #include <malloc.h> void m() {   char *p1;   p1 = malloc(100);   printf("開始對(duì)內(nèi)存進(jìn)行泄漏..."); }   int main() {     m();     return 0; }

如上代碼中,使用 malloc 申請(qǐng)了 100 個(gè)單位的內(nèi)存空間后,并沒有進(jìn)行釋放。假設(shè)該 m 函數(shù)在當(dāng)前系統(tǒng)中調(diào)用頻繁,那將會(huì)每次使用都將會(huì)造成 100  個(gè)單位的內(nèi)存空間不會(huì)釋放,久而久之就會(huì)造成嚴(yán)重的后果。理應(yīng)在 p1 使用完畢后添加 free 進(jìn)行釋放:

free(p1);

以下示范一個(gè)讀取文件時(shí)不規(guī)范的操作:

#include<stdio.h> #include<string.h> #include <malloc.h> int m(char *filename) {   FILE* f;  int key;   f = fopen(filename, "r");   fscanf(f, "%d", &key);   return key;  }   int main() {     m("number.txt");     return 0; }

以上文件在讀取時(shí)并沒有進(jìn)行  fclose,這時(shí)將會(huì)產(chǎn)生多余的內(nèi)存,可能一次還好,多次會(huì)增加成倍的內(nèi)存,可以使用循環(huán)進(jìn)行調(diào)用,之后在任務(wù)管理器中可查看該程序運(yùn)行時(shí)所占的內(nèi)存大小,代碼為:

#include<stdio.h> #include<string.h> #include <malloc.h> int m(char *filename) {   FILE* f;  int key;   f = fopen(filename, "r");   fscanf(f, "%d", &key);   return key;  }   int main() {  int i;  for(i=0;i<500;i++) {      m("number.txt");  }     return 0; }

可查看添加循環(huán)后的程序與添加循環(huán)前的程序做內(nèi)存占用的對(duì)比,就可以發(fā)現(xiàn)兩者之間添加了循環(huán)的代碼將會(huì)成本增加占用容量。

未被初始化的指針也會(huì)有可能造成內(nèi)存泄漏的情況,因?yàn)橹羔樜闯跏蓟赶虿豢煽?,如?/p>

int *p; *p = val;

包括錯(cuò)誤的釋放內(nèi)存空間:

pp=p; free(p);  free(pp);

釋放后使用,產(chǎn)生懸空指針。在申請(qǐng)了動(dòng)態(tài)內(nèi)存后,使用指針指向了該內(nèi)存,使用完畢后我們通過 free  函數(shù)釋放了申請(qǐng)的內(nèi)存,該內(nèi)存將會(huì)允許其它程序進(jìn)行申請(qǐng);但是我們使用過后的動(dòng)態(tài)內(nèi)存指針依舊指向著該地址,假設(shè)其它程序下一秒申請(qǐng)了該區(qū)域內(nèi)的內(nèi)存地址,并且進(jìn)行了操作。當(dāng)我依舊使用已  free 釋放后的指針進(jìn)行下一步的操作時(shí),或者所進(jìn)行了一個(gè)計(jì)算,那么將會(huì)造成的結(jié)果天差地別,或者是其它災(zāi)難性后果。所以對(duì)于這些指針在生存期結(jié)束之后也要置為  null。查看一個(gè)示例,由于 free 釋放后依舊使用該指針,造成的計(jì)算結(jié)果天差地別:

#include<stdio.h> #include<string.h> #include <malloc.h> int m(char *freep) {   int val=freep[0];  printf("2*freep=:%d\n",val*2);  free(freep);  val=freep[0];  printf("2*freep=:%d\n",val*2); }   int main() {  int *freep = (int *) malloc(sizeof (int));  freep[0]=1;  m(freep);     return 0;      }

以上代碼使用 malloc 申請(qǐng)了一個(gè)內(nèi)存后,傳值為 1;在函數(shù)中首先使用 val 值接收 freep 的值,將 val 乘 2,之后釋放  free,重新賦值給 val,最后使用 val 再次乘  2,此時(shí)造成的結(jié)果出現(xiàn)了極大的改變,而且最恐怖的是該錯(cuò)誤很難發(fā)現(xiàn),隱蔽性很強(qiáng),但是造成的后顧難以承受。運(yùn)行結(jié)果如下:

C語言/C++內(nèi)存管理是什么

三、 new 和 delete

C++ 中使用 new 和 delete 從堆中分配和釋放內(nèi)存,new 和 delete  是運(yùn)算符,不是函數(shù),兩者成對(duì)使用(后面說明為什么成對(duì)使用)。

new/delete 除了分配內(nèi)存和釋放內(nèi)存(與 malloc/free),還做更多的事情,所有在 C++ 中不再使用 malloc/free 而使用  new/delete。

3.1 new 和 delete 使用

new 一般使用格式如下:

  • 指針變量名 = new 類型標(biāo)識(shí)符;

  • 指針變量名 = new 類型標(biāo)識(shí)符(初始值);

  • 指針變量名 = new 類型標(biāo)識(shí)符[內(nèi)存單元個(gè)數(shù)];

在C++中new的三種用法包括:plain new, nothrow new 和 placement new。

plain new 就是我們最常使用的 new 的方式,在 C++ 中的定義如下:

void* operator new(std::size_t) throw(std::bad_alloc);   void operator delete( void *) throw();

plain new 在分配失敗的情況下,拋出異常 std::bad_alloc 而不是返回 NULL,因此通過判斷返回值是否為 NULL  是徒勞的。

char *getMemory(unsigned long size)    {         char * p = new char[size];        return p;  }    void main(void)    {     try{            char * p = getMemory(1000000);    // 可能發(fā)生異常         // ...            delete [] p;        }        catch(const std::bad_alloc &amp; ex)        {         cout &lt;&lt; ex.what();     }    }

nothrow new 是不拋出異常的運(yùn)算符new的形式。nothrow new在失敗時(shí),返回NULL。定義如下:

void * operator new(std::size_t, const std::nothrow_t&) throw(); void operator delete(void*) throw();
void func(unsinged long length)    {     unsinged char * p = new(nothrow) unsinged char[length];        // 在使用這種new時(shí)要加(nothrow) ,表示不使用異常處理 。       if (p == NULL)  // 不拋異常,一定要檢查         cout << "allocte failed !";            // ...        delete [] p; }

placement new 意即“放置”,這種new允許在一塊已經(jīng)分配成功的內(nèi)存上重新構(gòu)造對(duì)象或?qū)ο髷?shù)組。placement  new不用擔(dān)心內(nèi)存分配失敗,因?yàn)樗静环峙鋬?nèi)存,它做的唯一一件事情就是調(diào)用對(duì)象的構(gòu)造函數(shù)。定義如下:

void* operator new(size_t, void*); void operator delete(void*, void*);

palcement new 的主要用途就是反復(fù)使用一塊較大的動(dòng)態(tài)分配的內(nèi)存來構(gòu)造不同類型的對(duì)象或者他們的數(shù)組。placement  new構(gòu)造起來的對(duì)象或其數(shù)組,要顯示的調(diào)用他們的析構(gòu)函數(shù)來銷毀,千萬不要使用delete。

void main()    {       using namespace std;        char * p = new(nothrow) char [4];        if (p == NULL)        {         cout << "allocte failed" << endl;           exit( -1 );     }        // ...        long * q = new (p) long(1000);        delete []p;    // 只釋放 p,不要用q釋放。 }

p 和 q 僅僅是首址相同,所構(gòu)建的對(duì)象可以類型不同。所“放置”的空間應(yīng)小于原空間,以防不測(cè)。當(dāng)”放置new”超過了申請(qǐng)的范圍,Debug 版下會(huì)崩潰,但  Release 能運(yùn)行而不會(huì)出現(xiàn)崩潰!

該運(yùn)算符的作用是:只要第一次分配成功,不再擔(dān)心分配失敗。

void main()    {     using namespace std;        char * p = new(nothrow) char [100];        if (p == NULL)        {           cout << "allocte failed" << endl;         exit(-1);     }        long * q1 = new (p) long(100);        // 使用q1  ...        int * q2 = new (p) int[100/sizeof(int)];        // 使用q2 ...        ADT * q3 = new (p) ADT[100/sizeof(ADT)];        // 使用q3  然后釋放對(duì)象 ...        delete [] p;    // 只釋放空間,不再析構(gòu)對(duì)象。 }

注意:使用該運(yùn)算符構(gòu)造的對(duì)象或數(shù)組,一定要顯式調(diào)用析構(gòu)函數(shù),不可用 delete 代替析構(gòu),因?yàn)?placement new  的對(duì)象的大小不再與原空間相同。

void main()    {       using namespace std;        char * p = new(nothrow) char [sizeof(ADT)+2];        if (p == NULL)        {           cout << "allocte failed" &lt;&lt; endl;         exit(-1);      }      // ...      ADT * q = new (p) ADT;      // ...      // delete q; // 錯(cuò)誤     q->ADT::~ADT();  // 顯式調(diào)用析構(gòu)函數(shù),僅釋放對(duì)象     delete [] p;     // 最后,再用原指針來釋放內(nèi)存 }

placement new 的主要用途就是可以反復(fù)使用一塊已申請(qǐng)成功的內(nèi)存空間。這樣可以避免申請(qǐng)失敗的徒勞,又可以避免使用后的釋放。

特別要注意的是對(duì)于 placement new 絕不可以調(diào)用的 delete, 因?yàn)樵?new 只是使用別人替它申請(qǐng)的地方。釋放內(nèi)存是 nothrow  new 的事,即要使用原來的指針釋放內(nèi)存。free/delete 不要重復(fù)調(diào)用,被系統(tǒng)立即回收后再利用,再一次 free/delete  很可能把不是自己的內(nèi)存釋放掉,導(dǎo)致異常甚至崩潰。

上面提到 new/delete 比 malloc/free 多做了一些事情,new 相對(duì)于 malloc 會(huì)額外的做一些初始化工作,delete 相對(duì)于  free 多做一些清理工作。

class A {  public:      A()      {         cont<<"A()構(gòu)造函數(shù)被調(diào)用"<<endl;      }      ~A()      {         cont<<"~A()構(gòu)造函數(shù)被調(diào)用"<<endl;      } }

在 main 主函數(shù)中,加入如下代碼:

A* pa = new A();  //類 A 的構(gòu)造函數(shù)被調(diào)用 delete pa;        //類 A 的析構(gòu)函數(shù)被調(diào)用

可以看出:使用 new 生成一個(gè)類對(duì)象時(shí)系統(tǒng)會(huì)調(diào)用該類的構(gòu)造函數(shù),使用 delete  刪除一個(gè)類對(duì)象時(shí),系統(tǒng)會(huì)調(diào)用該類的析構(gòu)函數(shù)??梢哉{(diào)用構(gòu)造函數(shù)/析構(gòu)函數(shù)就意味著 new 和 delete 具備針對(duì)堆所分配的內(nèi)存進(jìn)行初始化和釋放的能力,而  malloc 和 free 不具備。

2.2 delete 與 delete[] 的區(qū)別

c++ 中對(duì) new 申請(qǐng)的內(nèi)存的釋放方式有 delete 和 delete[] 兩種方式,到底這兩者有什么區(qū)別呢?

我們通常從教科書上看到這樣的說明:

  • delete 釋放 new 分配的單個(gè)對(duì)象指針指向的內(nèi)存

  • delete[] 釋放 new 分配的對(duì)象數(shù)組指針指向的內(nèi)存 那么,按照教科書的理解,我們看下下面的代碼:

int *a = new int[10]; delete a;        //方式1 delete[] a;     //方式2

針對(duì)簡(jiǎn)單類型 使用 new 分配后的不管是數(shù)組還是非數(shù)組形式內(nèi)存空間用兩種方式均可 如:

int *a = new int[10]; delete a; delete[] a;

此種情況中的釋放效果相同,原因在于:分配簡(jiǎn)單類型內(nèi)存時(shí),內(nèi)存大小已經(jīng)確定,系統(tǒng)可以記憶并且進(jìn)行管理,在析構(gòu)時(shí),系統(tǒng)并不會(huì)調(diào)用析構(gòu)函數(shù)。

它直接通過指針可以獲取實(shí)際分配的內(nèi)存空間,哪怕是一個(gè)數(shù)組內(nèi)存空間(在分配過程中 系統(tǒng)會(huì)記錄分配內(nèi)存的大小等信息,此信息保存在結(jié)構(gòu)體  _CrtMemBlockHeader 中,具體情況可參看 VC 安裝目錄下 CRTSRCDBGDEL.cpp)。

針對(duì)類 Class,兩種方式體現(xiàn)出具體差異

當(dāng)你通過下列方式分配一個(gè)類對(duì)象數(shù)組:

class A    {     private:       char *m_cBuffer;       int m_nLen;     `` public:       A(){ m_cBuffer = new char[m_nLen]; }       ~A() { delete [] m_cBuffer; }    };     A *a = new A[10];    delete a;         //僅釋放了a指針指向的全部?jī)?nèi)存空間 但是只調(diào)用了a[0]對(duì)象的析構(gòu)函數(shù) 剩下的從a[1]到a[9]這9個(gè)用戶自行分配的m_cBuffer對(duì)應(yīng)內(nèi)存空間將不能釋放 從而造成內(nèi)存泄漏    delete[] a;      //調(diào)用使用類對(duì)象的析構(gòu)函數(shù)釋放用戶自己分配內(nèi)存空間并且   釋放了a指針指向的全部?jī)?nèi)存空間

所以總結(jié)下就是,如果 ptr 代表一個(gè)用new申請(qǐng)的內(nèi)存返回的內(nèi)存空間地址,即所謂的指針,那么:

delete ptr 代表用來釋放內(nèi)存,且只用來釋放 ptr 指向的內(nèi)存。delete[] rg  用來釋放rg指向的內(nèi)存,!!還逐一調(diào)用數(shù)組中每個(gè)對(duì)象的destructor!!

對(duì)于像 int/char/long/int*/struct 等等簡(jiǎn)單數(shù)據(jù)類型,由于對(duì)象沒有 destructor ,所以用 delete 和 delete  []是一樣的!但是如果是 C++ 對(duì)象數(shù)組就不同了!

關(guān)于 new[] 和 delete[],其中又分為兩種情況:

(1) 為基本數(shù)據(jù)類型分配和回收空間;

(2) 為自定義類型分配和回收空間;

對(duì)于 (1),上面提供的程序已經(jīng)證明了 delete[] 和 delete 是等同的。但是對(duì)于 (2),情況就發(fā)生了變化。

我們來看下面的例子,通過例子的學(xué)習(xí)了解 C++ 中的 delete 和 delete[] 的使用方法

#include <iostream> using namespace std;  class Babe { public:     Babe()     {         cout << \"Create a Babe to talk with me\" << endl;     }      ~Babe()     {         cout << \"Babe don\'t Go away,listen to me\" << endl;     } };  int main() {     Babe* pbabe = new Babe[3];     delete pbabe;     pbabe = new Babe[3];     delete[] pbabe;     return 0; }

結(jié)果是:

Create a babe to talk with me Create a babe to talk with me Create a babe to talk with me Babe don\'t go away,listen to me Create a babe to talk with me Create a babe to talk with me Create a babe to talk with me Babe don\'t go away,listen to me Babe don\'t go away,listen to me Babe don\'t go away,listen to me

大家都看到了,只使用 delete 的時(shí)候只出現(xiàn)一個(gè) Babe don&rsquo;t go away,listen to me,而使用 delete[] 的時(shí)候出現(xiàn)  3 個(gè) Babe don&rsquo;t go away,listen to me。不過不管使用 delete 還是 delete[]  那三個(gè)對(duì)象的在內(nèi)存中都被刪除,既存儲(chǔ)位置都標(biāo)記為可寫,但是使用 delete 的時(shí)候只調(diào)用了 pbabe[0] 的析構(gòu)函數(shù),而使用了 delete[] 則調(diào)用了  3 個(gè) Babe 對(duì)象的析構(gòu)函數(shù)。

你一定會(huì)問,反正不管怎樣都是把存儲(chǔ)空間釋放了,有什么區(qū)別。

答:關(guān)鍵在于調(diào)用析構(gòu)函數(shù)上。此程序的類沒有使用操作系統(tǒng)的系統(tǒng)資源(比如:Socket、File、Thread等),所以不會(huì)造成明顯惡果。如果你的類使用了操作系統(tǒng)資源,單純把類的對(duì)象從內(nèi)存中刪除是不妥當(dāng)?shù)?,因?yàn)闆]有調(diào)用對(duì)象的析構(gòu)函數(shù)會(huì)導(dǎo)致系統(tǒng)資源不被釋放,這些資源的釋放必須依靠這些類的析構(gòu)函數(shù)。所以,在用這些類生成對(duì)象數(shù)組的時(shí)候,用  delete[] 來釋放它們才是王道。而用 delete 來釋放也許不會(huì)出問題,也許后果很嚴(yán)重,具體要看類的代碼了。

“C語言/C++內(nèi)存管理是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(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)容。

c++
AI