文章目錄
- 前言
- 一、C/C++內存分布
- 二、C語言中動態內存管理方式
- 三.C++的內存管理方式
- new/delete操作內置類型
- new/delete操作自定義類型
- 四.定位new
- 總結
前言
在一行一行的代碼之中,不同的數據存放的位置是有所不同的,正是因為這些數據的性質不同,所以才被安排存放在不同的區域
所以,身為預備程序員,要能夠分辨出不同的數據所存放的位置。
以下是本篇文章正文內容,下面案例可供參考
一、C/C++內存分布
我們先來看下面的一段代碼和相關問題
int golbaval = 1;
static int staticGlobalvar = 1;
void test()
{static int staticval = 1;int localvar = 1;int num1[10] = {1,2,3,4};char char2[] = "abcd";const char * pchar3 = "abcd";int * p1 = (int *)malloc(sizeof(int)*4);int * p2 = (int *)calloc(4,sizeof(int));int * p3 = (int *)realloc(p2,sizeof(int)*4);free(p1);free(p3);}
代碼如上,題目如下:
選項A.棧 B.堆 C.數據段(靜態區)D.代碼段(常量區),下面變量分別存放在哪里?
globavar(C)
staticGlobaval(C)
staticval(C)
localvar(A)
num1(A)
——往全局中看,globavar與statGlobaval均是在全局中定義的,所以在全局中定義的,均屬于靜態區
——static修飾的變量不管在哪里定義都位于靜態區,所以在Test函數的staticval也位于靜態區。
——locavar與num1都是在局部定義的變量,出了這個Test函數,兩者均會被銷毀,生命周期較短,所以這兩者均屬于棧。
接下來,是一些比較難的。
char2(A)
*char2(A)
pchar3(A)
*pchar3(D)
p1(A)
*p1(B)
我們可以看到,char2是數組名,char2的空間其實是有5個空間,最后一個空間留給了‘\0’,我們都知道,字符串都是位于常量區的,但是char是我們在棧中定義出來的數組名,也是個變量,相當于常量區中的"abcd\0"拷貝了一份給了char2,所以char2是位于棧中的,*char2也是位于棧中的,因為是從常量區拷貝一份給了棧上的char2。
pchar3是一個指針,它指向的位置是指向位于常量區的字符串"abcd\0",pchar3是我們在棧中定義的一個指針變量,所以它位于棧中,*pchar3就是這塊位于常量區的"abcd\0"它位于代碼段(常量區)
p1是我們在棧中定義的一個指針變量,與pchar3性質相同,所以它是位于棧上的一個變量,這個指針變量指向的這塊空間是位于堆上開辟的,所以p1在棧上,*p1位于堆上。
【說明】
1.棧又叫堆棧-非靜態成員變量/函數參數/返回值等等,棧是向下生長的。
2.堆用于程序運行時動態的內存分配,一般來說堆是向上身上的,也不排除有例外是向下身上。
3.數據段–存儲全局數據和靜態數據(像我們在全局中定義的變量以及我們使用static關鍵字修飾的變量)
4.代碼段–可執行的代碼/只讀常量
二、C語言中動態內存管理方式
之前我們在前面的學習中就已經對動態內存開辟有了一定的了解,現在讓我們回顧一下吧。
1.void* malloc(size_t size)
括號中輸入的是需要向堆申請多少字節的空間,例如我們想向堆中申請5個int類型的空間,也就是20個字節,那么我們可以這樣描述。(int )malloc(5sizeof(int))。
2.void * calloc(size_t n nemb,size_t size)
跟malloc類似,前面的是元素個數,后面是單個元素字節數,同樣我們向堆中申請5個int類型的空間,用calloc是這樣表示的,(int *)calloc(5,sizeof(int))。
注:calloc會將申請的空間全部初始化為0
3.void *realloc(void * prt,size_t size)
①realloc是用來調整之前申請的內存的空間大小,所以待調整的內存必須是之前在堆上分配的空間。
②prt是指向原先內存的指針,size是新內存字節數,返回的是指向新內存的指針,故需要強轉。
③申請失敗會返回NULL,所以需要檢查申請是否成功。
void test()
{int *p2 = (int *)calloc(4,sizeof(int));int *p3 = (int *)realloc(p2,sizeof(int)*10);//這里需要free(p2)嗎?
}
這里是不需要free(p2)的,p2是指向我們calloc申請的空間,p3則是指向我們新內存的空間,以上代碼是對原先的空間進行擴展。
假設申請失敗,則返回空指針。如果申請成功,分為兩種情況。
①直接在原先的空間后面追加需要添加的空間,這種情況下,p2就跟p3相同,所以free(p3)就等同于free(p2)。
②后面的空間不允許你進行擴展,那么編譯器就會向堆上重新申請一塊大的空間(這塊空間的大小與我們擴展后的空間大小一致),然后把舊空間上的數據拷貝到新申請的大空間里,然后釋放舊空間,即此類情況,編譯器會自動幫我們釋放p2。
三.C++的內存管理方式
C語言的內存管理方式在C++上同時適用,但是在一些場景上C語言就顯得有點力不從心,所以C++推出了屬于自己的內存管理方式。通過new以及delete進行操作。
new/delete操作內置類型
void test()
{//申請一塊int類型的空間int * p4 = new int;//申請一塊int類型的空間,并將其初始化為10int * p5 = new int(10);//動態申請10個int類型的空間int * p6 = new int[10];//釋放空間delete p4;delete p5;delete []p6;
}
這里要對應上,你是new就delete,你是new[],就delete[]。不然容易出錯。
new/delete操作自定義類型
class A
{
public:A(int a = 0):_a(a){cout<<"A()"<<endl;}~A(){cout<<"~A()"<<endl;}
}
private:int _a;
int main()
{A * p1 = malloc(sizeof(A));A * p2 = new int(1);free(p1);delete p2;//內置類型幾乎沒有區別int * p3 = (int *)malloc(sizeof(A));int * p4 = new int;free(p3);delete p4;return 0;
}
class Stack
{
public:Stack(int n = 4):_a(new int[n]),_size(0)_capacity(n){cout << "Stack()"<<endl;}~Stack(){free(_a);_size = _capacity = 0;}
private:int _a;size_t _size;size_t _capacity;
}
int main()
{Stack * p1 = (Stack *)malloc(sizeof(A);Stack * p2 = new Stack(10);free(p1);delete p2;
}
直接上結論:new/delete與malloc/free之間的區別是,前者new的過程中會調用構造函數,delete會調用析構,而后者沒有執行此操作。
★new/delete的本質
new和delete相比于malloc/free而言,new會在malloc的基礎上多了一次構造函數,delete會在free清理資源(也就是析構函數)的基礎上,再次釋放對象。這里我畫個圖,以Stack這個類舉例
由圖可知,在棧這個類里面,new會先對棧對象的整體大小進行一個計算,開出一個能夠存放棧三個成員變量的空間,然后再調用構造函數對這個對象進行初始化。最后來到了delete,delete先把向堆上申請的空間資源進行一個清理,再對我們這個對象空間給釋放。
簡單講,new就是operator new + 構造,delete就是operator delete + 析構。
new原理:
operator new函數申請空間。
在申請的空間上執行構造函數,完成對象的構造。
delete原理:
在空間上執行析構函數,完成對象中資源的清理工作。
調用operator delete函數釋放對象的空間。
注意:new在創建對象的時候,創建失敗要拋異常。
四.定位new
定位new是一種特殊的new,前面我們已經學過了普通的new,現在跟著我來看看這兩者的區別吧。
簡單一句話,普通new就是創建對象+構造,定位new就只有構造。
int main()
{A * p1 = (A*)malloc(sizeof(A));//這里p1只是指向了這塊空間,還不能算作是關于A的對象,因為它還沒有初始化。new(p1)A;//這一步就是對A的一個構造函數,此處也可傳參參與構造p1->~A();//需手動進行析構清理資源(對象自身管理的資源)free(p1);//釋放對象本身所在的內存塊//下面案例更加具體A * p2 = (A*)operator new(sizeof(A));//operator new參與創建對象本身的空間new(p2)A(10);//這里使用定位new并且傳參對對象進行構造p2->~A();free(p2);
}
總結
本文對內存管理進行了一個簡單的講解。new/delete相比于我們之前傳統的內存管理方法要更方便,這里鼓勵大家多使用new/delete進行內存的一個開發。