目录
2.C语言中动态内存管理方式:malloc/calloc/realloc/free (回顾)
4.operator new与operator delete函数
6. 定位new表达式(placement-new) (了解)
前言
本节内容将对C与C++的内存管理方面的知识进行分析和解释。
1.C/C++内存分布
在这里,我们先对C语言的内存分布进行一个回顾。
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.数据段(静态区) D.代码段(常量区)globalVar在哪里?__C__staticGlobalVar在哪里?_C___staticVar在哪里?__C__localVar在哪里?___A_num1 在哪里?__A__char2在哪里?__A__*char2在哪里?__A_pChar3在哪里?__A__*pChar3在哪里?___Dptr1在哪里?_A___*ptr1在哪里?__B__
详细的图解如下
【说明】
1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)3. 堆用于程序运行时动态内存分配,堆是可以上增长的。4. 数据段--存储全局数据和静态数据。5. 代码段--可执行的代码/只读常量。
2.C语言中动态内存管理方式:malloc/calloc/realloc/free (回顾)
void Test ()
{
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
free(p3 );
}
1. malloc:
- 语法:void* malloc(size_t size);
- 功能:分配指定字节数的内存,但不会初始化内存。分配的内存包含不确定的值。
- 示例:int* p = (int*)malloc(4 * sizeof(int));
2. calloc:
- 语法:void* calloc(size_t num, size_t size);
- 功能:分配内存用于数组,并初始化所有位为0。num`是元素数量,size是每个元素的大小。
- 示例:int* p = (int*)calloc(4, sizeof(int));(分配4个整数并初始化为0)
3. realloc:
- 语法:void* realloc(void* ptr, size_t size);
- 功能:重新调整已分配内存的大小。如果 ptr 是 NULL,效果等同于 malloc;如果 size 为0,用法等同于 `free`。如果扩展内存,原有数据将被保留。
- 示例:int* p = (int*)realloc(p2, sizeof(int) * 10);` 对于你的代码片段:
不用释放p2
- 使用 calloc 分配内存后,p2指向这块内存。 - 当你使用 `realloc` 调整 `p2` 的大小时,`realloc` 会返回一个新的指针 `p3`,如果内存调整成功,原来 `p2` 的内存会被释放,因此你不需要再对 `p2` 调用 `free`。
3.C++内存管理方式
3.1new/delete操作内置类型
#include <iostream>
using namespace std;
int main() {
//动态申请一个int类型的空间并初始化为520
int* p1 = new int(520);
动态申请3个int类型的空间并初始化
int* p2 = new int[3] {1, 2, 3};
cout << *p1 << endl;
for (int i = 0; i < 3; ++i) {
cout << "p2[" << i << "]: " << p2[i] << endl;
}
// 释放内存
delete p1;
delete[] p2;
return 0;
}
注意事项
-
内存管理:
- 动态分配的内存需要手动释放,以防止内存泄漏。
- 使用
delete
释放单个对象内存,使用delete[]
释放数组内存。
-
初始化方式:
- 在 C++11 及以后,可以使用初始化列表对动态数组进行初始化,如你所示的
{1, 2, 3}
。 - 在较旧的 C++ 版本中,动态数组的初始化可以通过循环来完成,因为不支持在
new
语句中直接初始化数组。
- 在 C++11 及以后,可以使用初始化列表对动态数组进行初始化,如你所示的
3.2new/delete操作自定义类型
#include <iostream>
using namespace std;
class A {
public:
A(int a=0 , int b=0)
:_a(a), _b(b) {
cout << "A(int a = 0, int b = 0)" << endl;
}
~A(){
cout << "~A()" << endl;
}
//void Print(){
// cout << "A::Print->" << _a << endl;
//}
private:
int _a = 1;
int _b = 1;
};
int main()
{
A* p1 = new A(520);
A* p2 = new A(520,1314);
delete p1;
delete p2;
//该自定义类型初始化方法
A aa1(1, 1);
A aa2(2, 2);
A aa3(3, 3);
A* p3 = new A[3]{aa1, aa2, aa3};
delete[]p3;
//匿名初始化
A* p4 = new A[3]{ A(1,1), A(2,2), A(3,3)};
delete[]p4;
//列表初始化
A* p5 = new A[3]{ {1,1}, {2,2}, {3,3} };
delete []p5;
return 0;
}
4.operator new与operator delete函数
/*operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。*/void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc){// try to allocate size bytesvoid *p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);}/*operator delete: 该函数最终是通过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_FINALLYreturn;}/*free的实现*/#define free(p) _free_dbg(p, _NORMAL_BLOCK)
抛异常补充:
void func()
{ // throw try/catch
int n = 1;
while (1)
{
void* p1 = new char[1024 * 1024];
cout << p1 << "->"<< n<<endl;
++n;
}
}
int main()
{
try
{
func();
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
1. 抛出异常
使用 throw
关键字可以抛出一个异常。异常可以是任何类型,通常是 std::exception
或其派生类。
2. 捕获异常
使用 try
和 catch
块来捕获和处理异常。
#include <iostream>
#include <stdexcept> // runtime_error
using namespace std;
class A {
public:
A(int value) {
if (value < 0) {
throw runtime_error("Value cannot be negative");
}
_value = value;
}
void Print() {
cout << "Value: " << _value << endl;
}
private:
int _value;
};
int main() {
try {
A obj1(10);
obj1.Print();
A obj2(-5); // 这将抛出异常
obj2.Print();
}
catch (const exception& e) {
cout << "Exception caught: " << e.what() << endl;
}
return 0;
}
5.new和delete的实现原理
5.1 内置类型
5.2 自定义类型
6. 定位new表达式(placement-new) (了解)
#include <iostream>
using namespace std;
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现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
p1->~A();
free(p1);
A * p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
return 0;
}
7. malloc/free和new/delete的区别
结束语
本节内容到此结束,最后感谢各位友友的支持,点个赞吧!!!