?? 作者簡介 :RO-BERRY
?? 學(xué)習(xí)方向:致力于C、C++、數(shù)據(jù)結(jié)構(gòu)、TCP/IP、數(shù)據(jù)庫等等一系列知識
?? 日后方向 : 偏向于CPP開發(fā)以及大數(shù)據(jù)方向,歡迎各位關(guān)注,謝謝各位的支持
1. C/C++內(nèi)存分布
我們先來看下面的一段代碼和相關(guān)問題
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
-
選擇題:
選項: A.棧 B.堆 C.數(shù)據(jù)段(靜態(tài)區(qū)) D.代碼段(常量區(qū))globalVar在哪里?____ staticGlobalVar在哪里?____
staticVar在哪里?____ localVar在哪里?____
num1 在哪里?____答案為CCCAA
globalVar
為定義為全局變量,所以在靜態(tài)區(qū)staticGlobalVar
定義為static全局變量,在靜態(tài)區(qū),并且只在本文件可以使用,其他文件查找不到staticVar
定義為函數(shù)體內(nèi)的static變量,在靜態(tài)區(qū),并且只在本函數(shù)內(nèi)可以使用,其他函數(shù)查找不到localVar
定義為函數(shù)體內(nèi)的整形,為棧區(qū),函數(shù)結(jié)束銷毀num1
定義為函數(shù)體內(nèi)的數(shù)組,為棧區(qū),函數(shù)結(jié)束銷毀char2在哪里?____ *char2在哪里?___
pChar3在哪里?____ *pChar3在哪里?____
ptr1在哪里?____ *ptr1在哪里?____答案為AAAD
char2
定義為函數(shù)體內(nèi)的字符數(shù)組,為棧區(qū),函數(shù)結(jié)束銷毀*char2
為字符數(shù)組里的字符,為棧區(qū),函數(shù)結(jié)束銷毀pChar3
為指針變量,存的是常量區(qū)的地址,為棧區(qū),函數(shù)結(jié)束銷毀*pChar3
為常量字符串/字面量,存在常量區(qū)ptr1
為指針變量,存的是堆區(qū)的地址,但是為棧區(qū),函數(shù)結(jié)束銷毀*ptr1
為堆區(qū) -
填空題:
sizeof(num1) = ____;
sizeof(char2) = ____; strlen(char2) = ____;
sizeof(pChar3) = ____; strlen(pChar3) = ____;
sizeof(ptr1) = ____;num1
為十個整形的數(shù)組,大小為40char2
為字符數(shù)組,存儲的是“abcd\0”,大小為5char2
的長度為4pChar3
的大小為4或者8,看為32位還是64位pChar3
的長度為4ptr1
的大小為4或者8 -
sizeof 和 strlen 區(qū)別?
sizeof和strlen是C語言中的兩個關(guān)鍵字,用于獲取變量或字符串的大小。它們的區(qū)別如下:
-
sizeof:sizeof是一個運算符,用于獲取變量或數(shù)據(jù)類型的大?。ㄒ宰止?jié)為單位)。它可以用于任何數(shù)據(jù)類型,包括基本數(shù)據(jù)類型(如int、float)和自定義數(shù)據(jù)類型(如結(jié)構(gòu)體、數(shù)組)。sizeof返回的是一個無符號整數(shù)值。
-
strlen:strlen是一個函數(shù),用于獲取字符串的長度(以字符為單位)。它只能用于字符串類型(即以null字符’\0’結(jié)尾的字符數(shù)組)。strlen返回的是一個整數(shù)值,表示字符串中字符的個數(shù),不包括null字符。
總結(jié)一下:
sizeof用于獲取變量或數(shù)據(jù)類型的大小,而strlen用于獲取字符串的長度。
sizeof可以用于任何數(shù)據(jù)類型,而strlen只能用于字符串類型。
sizeof返回的是字節(jié)數(shù),strlen返回的是字符個數(shù)。 -
【說明】
- 棧又叫堆棧–非靜態(tài)局部變量/函數(shù)參數(shù)/返回值等等,棧是向下增長的。
- 內(nèi)存映射段是高效的I/O映射方式,用于裝載一個共享的動態(tài)內(nèi)存庫。用戶可使用系統(tǒng)接口創(chuàng)建共享共享內(nèi)存,做進程間通信。
- 堆用于程序運行時動態(tài)內(nèi)存分配,堆是可以上增長的。
- 數(shù)據(jù)段–存儲全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)。
- 代碼段–可執(zhí)行的代碼/只讀常量。
2. C語言中動態(tài)內(nèi)存管理方式:malloc/calloc/realloc/free
void Test()
{
int* p1 = (int*)malloc(sizeof(int));
free(p1);
int* p2 = (int*)calloc(4, sizeof(int));
int* p3 = (int*)realloc(p2, sizeof(int) * 10);
// 這里需要free(p2)嗎?
free(p3);
}
在這段代碼中,需要使用free(p2)來釋放內(nèi)存。雖然在后續(xù)使用realloc重新分配內(nèi)存時,會將原來的內(nèi)存塊復(fù)制到新的內(nèi)存塊中,但是在這之前,我們?nèi)匀恍枰褂胒ree來釋放原來的內(nèi)存塊。這是因為realloc函數(shù)并不會自動釋放原來的內(nèi)存塊,而是返回一個新的指針指向重新分配后的內(nèi)存塊。所以,在使用realloc之前,我們需要手動釋放原來的內(nèi)存塊。
.【面試題】
- malloc/calloc/realloc的區(qū)別?
malloc、calloc和realloc都是C語言中用于動態(tài)存分配的函數(shù),它們的區(qū)別如下:
- malloc:malloc函數(shù)用于分配指定大小的內(nèi)存塊,并返回一個指向該內(nèi)存塊起始地址的指針。它只負責(zé)分配內(nèi)存,并不對內(nèi)存進行初始化,所以分配的內(nèi)存中可能包含舊數(shù)據(jù)。
- calloc:calloc函數(shù)也用于分配內(nèi)存塊,但與malloc不同的是,它在分配內(nèi)存后會將內(nèi)存塊中的每個字節(jié)都初始化為0。因此,使用calloc分配的內(nèi)存是被清零的。
- realloc:realloc函數(shù)用于重新分配已經(jīng)分配的內(nèi)存塊的大小。它接受兩個參數(shù):一個已經(jīng)分配的內(nèi)存塊指針和新的大小。realloc會嘗試重新分配指定大小的內(nèi)存,并返回一個指向新分配內(nèi)存起始地址的指針。如果重新分配成功,原來的內(nèi)存塊會被釋放;如果失敗,原來的內(nèi)存塊保持不變。需要注意的是,realloc可能會將原來的數(shù)據(jù)復(fù)制到新的內(nèi)存塊中,因此在使用realloc時需要小心處理指針引用問題。
3. C++內(nèi)存管理方式
C語言內(nèi)存管理方式在C++中可以繼續(xù)使用,但有些地方就無能為力,而且使用起來比較麻煩,因
此C++又提出了自己的內(nèi)存管理方式:通過new和delete操作符進行動態(tài)內(nèi)存管理。
3.1 new/delete操作內(nèi)置類型
void Test()
{
// 動態(tài)申請一個int類型的空間
int* ptr4 = new int;
// 動態(tài)申請一個int類型的空間并初始化為10
int* ptr5 = new int(10);
// 動態(tài)申請3個int類型的空間
int* ptr6 = new int[3];
// 動態(tài)申請3個int類型的空間,并初始化為1,2,3
int* ptr7 = new int[3]{1,2,3};
// 動態(tài)申請10個int類型的空間,并初始化為1,2,3,0,0,0,0,0,0
int* ptr8 = new int[10]{1,2,3}; //new會為你沒有初始化的空間默認(rèn)初始化為0
delete ptr4;
delete ptr5;
delete[] ptr6;
delete[] ptr7;
delete[] ptr7;
}
注意:申請和釋放單個元素的空間,使用new和delete操作符,申請和釋放連續(xù)的空間,使用new[]和delete[] --------注意:匹配起來使用。
3.2 new和delete操作自定義類型
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
// new/delete 和 malloc/free最大區(qū)別是 new/delete對于【自定義類型】除了開空間還會調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)
cout << "自定義類型" << endl;
cout << "malloc and free" << endl;
A* p1 = (A*)malloc(sizeof(A));
free(p1);
cout << "new and delete" << endl;
A* p2 = new A(1);
delete p2;
cout << endl;
// 內(nèi)置類型是幾乎是一樣的
cout << "內(nèi)置類型" << endl;
cout << "malloc and free" << endl;
int* p3 = (int*)malloc(sizeof(int)); // C
free(p3);
cout << "new and delete" << endl;
int* p4 = new int;
delete p4;
cout << endl;
cout << "自定義類型多個空間" << endl;
cout << "malloc and free" << endl;
A* p5 = (A*)malloc(sizeof(A) * 10);
free(p5);
cout << "new and delete" << endl;
A* p6 = new A[10];
delete[] p6;
cout << endl;
return 0;
}
注意:在申請自定義類型的空間時,new會調(diào)用構(gòu)造函數(shù),delete會調(diào)用析構(gòu)函數(shù),而malloc與free不會。 所以對于自定義類型的初始化,new和delete比malloc和free更為方便
內(nèi)置類型的對象申請釋放,new和malloc除了用法上,沒有人任何區(qū)別
4. operator new與operator delete函數(shù)
new和delete是用戶進行動態(tài)內(nèi)存申請和釋放的操作符,operator new 和operator delete是系統(tǒng)提供的全局函數(shù),new在底層調(diào)用operator new全局函數(shù)來申請空間,delete在底層通過operator delete全局函數(shù)來釋放空間。這兩個函數(shù)是封裝了malloc和free。
/*
operator new:該函數(shù)實際通過malloc來申請空間,當(dāng)malloc申請空間成功時直接返回;申請空間
失敗,嘗試執(zhí)行空 間不足應(yīng)對措施,如果改應(yīng)對措施用戶設(shè)置了,則繼續(xù)申請,否
則拋異常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void* p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申請內(nèi)存失敗了,這里會拋出bad_alloc 類型異常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/*
operator delete: 該函數(shù)最終是通過free來釋放空間的
*/
void operator delete(void* pUserData)
{
_CrtMemBlockHeader* pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的實現(xiàn)
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
operator new 實際也是通過malloc來申請空間,如果malloc申請空間成功就直接返回,否則執(zhí)行用戶提供的空間不足應(yīng)對措施,如果用戶提供該措施就繼續(xù)申請,否則就拋異常。operator delete 最終是通過free來釋放空間的。
5. new和delete的實現(xiàn)原理
5.1 內(nèi)置類型
如果申請的是內(nèi)置類型的空間,new和malloc,delete和free基本類似,不同的地方是:
new/delete申請和釋放的是單個元素的空間,new[]和delete[]申請的是連續(xù)空間,而且new在申請空間失敗時會拋異常,malloc會返回NULL。
5.2 自定義類型
new的原理
- 調(diào)用operator new函數(shù)申請空間
- 在申請的空間上執(zhí)行構(gòu)造函數(shù),完成對象的構(gòu)造
delete的原理
- 在空間上執(zhí)行析構(gòu)函數(shù),完成對象中資源的清理工作
- 調(diào)用operator delete函數(shù)釋放對象的空間
new T[N]的原理
- 調(diào)用operator new[]函數(shù),在operator new[]中實際調(diào)用operator new函數(shù)完成N個對象空間的申請
- 在申請的空間上執(zhí)行N次構(gòu)造函數(shù)
delete[]的原理
- 在釋放的對象空間上執(zhí)行N次析構(gòu)函數(shù),完成N個對象中資源的清理
- 調(diào)用operator delete[]釋放空間,實際在operator delete[]中調(diào)用operator delete來釋放空間
6. 定位new表達式(placement-new) (了解)
定位new表達式是在已分配的原始內(nèi)存空間中調(diào)用構(gòu)造函數(shù)初始化一個對象。
使用格式:
new (place_address) type
或者new (place_address) type(initializer-list)
place_address
必須是一個指針,initializer-list
是類型的初始化列表
使用場景:
定位new表達式在實際中一般是配合內(nèi)存池使用。因為內(nèi)存池分配出的內(nèi)存沒有初始化,所以如果是自定義類型的對象,需要使用new的定義表達式進行顯示調(diào)構(gòu)造函數(shù)進行初始化。
class Stack
{
public:
Stack(int capacity = 4)
{
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
_a = new int[capacity];
_top = 0;
_capacity = capacity;
}
~Stack()
{
cout << "調(diào)用析構(gòu)函數(shù)" << endl;
delete[] _a;
_a = nullptr;
_top = 0;
_capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
int main()
{
//構(gòu)造函數(shù)自動調(diào)用
Stack s1;
Stack* p1 = (Stack*)operator new(sizeof(Stack));
//不能這么顯示調(diào)用構(gòu)造函數(shù)
//p1->Stack(1);
//定位new可以顯示調(diào)用構(gòu)造函數(shù)
new(p1)Stack(1);
//析構(gòu)函數(shù)可以顯示調(diào)用
p1->~Stack();
return 0;
}
運用實例:
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
// 定位new/replacement new
int main()
{
// p1現(xiàn)在指向的只不過是與A對象相同大小的一段空間,還不能算是一個對象,因為構(gòu)造函數(shù)沒有執(zhí)行
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; // 注意:如果A類的構(gòu)造函數(shù)有參數(shù)時,此處需要傳參
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
return 0;
}
7. 常見面試題
7.1 malloc/free和new/delete的區(qū)別
malloc/free和new/delete的共同點: 都是從堆上申請空間,并且需要用戶手動釋放。
不同的地方是:
- malloc和free是函數(shù),new和delete是操作符
- malloc申請的空間不會初始化,new可以初始化
- malloc申請空間時,需要手動計算空間大小并傳遞,new只需在其后跟上空間的類型即可,
如果是多個對象,[]中指定對象個數(shù)即可 - malloc的返回值為void*, 在使用時必須強轉(zhuǎn),new不需要,因為new后跟的是空間的類型
- malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需
要捕獲異常 - 申請自定義類型對象時,malloc/free只會開辟空間,不會調(diào)用構(gòu)造函數(shù)與析構(gòu)函數(shù),而new
在申請空間后會調(diào)用構(gòu)造函數(shù)完成對象的初始化,delete在釋放空間前會調(diào)用析構(gòu)函數(shù)完成
空間中資源的清理
7.2 內(nèi)存泄漏
什么是內(nèi)存泄漏: 內(nèi)存泄漏指因為疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并不是指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,因為設(shè)計錯誤,失去了對該段內(nèi)存的控制,因而造成了內(nèi)存的浪費。
內(nèi)存泄漏的危害: 長期運行的程序出現(xiàn)內(nèi)存泄漏,影響很大,如操作系統(tǒng)、后臺服務(wù)等等,出現(xiàn)內(nèi)存泄漏會導(dǎo)致響應(yīng)越來越慢,最終卡死。
7.3 operator new和new的區(qū)別
operator new和new是C++中用于動態(tài)內(nèi)存分配的兩個關(guān)鍵字,它們有以下區(qū)別:
-
operator new是一個全局函數(shù),用于分配指定大小的內(nèi)存塊,但不會調(diào)用對象的構(gòu)造函數(shù)。它返回一個指向分配內(nèi)存的指針。使用operator new時,需要手動調(diào)用對象的構(gòu)造函數(shù)來初始化對象。
-
new是一個運算符,用于在動態(tài)存儲區(qū)中分配內(nèi)存,并調(diào)用對象的構(gòu)造函數(shù)進行初始化。它可以自動計算所需的內(nèi)存大小,并返回一個指向已初始化對象的指針。文章來源:http://www.zghlxwxcb.cn/news/detail-837571.html
總結(jié)來說,operator new只負責(zé)內(nèi)存的分配,而new除了分配內(nèi)存外還會調(diào)用對象的構(gòu)造函數(shù)進行初始化。文章來源地址http://www.zghlxwxcb.cn/news/detail-837571.html
到了這里,關(guān)于【C++庖丁解?!緾++內(nèi)存管理 | new和delete的使用以及使用原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!