网站建设新规,凡科建站登录入口官方正版,app开发编程,论坛做视频网站有哪些文章目录 :dizzy: C/C内存分布:dizzy:C语言中动态内存管理方式 :sparkles:malloc :sparkles:calloc :sparkles:reallocfree :dizzy:C语言中动态内存管理方式 :sparkles:new和delete操作内置类型 :sparkles:new和delete操作自定义类型 :dizzy:operator new与ope… 文章目录 :dizzy: C/C内存分布:dizzy:C语言中动态内存管理方式 :sparkles:malloc :sparkles:calloc :sparkles:reallocfree :dizzy:C语言中动态内存管理方式 :sparkles:new和delete操作内置类型 :sparkles:new和delete操作自定义类型 :dizzy:operator new与operator delete函数:dizzy:new和delete的实现原理 :sparkles:内置类型 :sparkles:自定义类型 :dizzy: 定位new表达式(placement-new) C/C内存分布 在认识内存管理之前我们需要先认识C/C内存分布首先声明一点这里的所讲的内存分布并不是物理内存的分布而是逻辑上的内存分布逻辑内存与物理内存会以某种方式进行联系这里不做过多说明仅做初步了解。 见下图 还是不够直观怎么证明内存分布是如图形式 1 #includestdio.h2 #includestdlib.h3 int g_val100;4 int g_unval; 5 static int g_s_val200;6 static int g_s_uval;7 int main(int argc,char*argv[],char*env[])8 {9 static int s_val100;10 const char* phello world;11 char *heap1(char*)malloc(10);12 char *heap2(char*)malloc(10);13 char *heap3(char*)malloc(10);14 char *heap4(char*)malloc(10);15 printf(argv addr: %p\n,argv[0]);16 printf(env addr: %p\n,env[0]);17 18 printf(stack addr: %p\n,heap1);19 printf(stack addr: %p\n,heap2);20 printf(stack addr: %p\n,heap3);21 printf(stack addr: %p\n,heap4);22 23 printf(heap addr: %p\n,heap1);24 printf(heap addr: %p\n,heap2);25 printf(heap addr: %p\n,heap3);26 printf(heap addr: %p\n,heap3);27 28 printf(global unval addr: %p\n,g_unval);29 printf(global unval addr: %p\n,g_s_uval);30 printf(global val addr: %p\n,g_s_val);31 printf(global val addr: %p\n,g_val);32 printf(global val addr: %p\n,s_val);33 printf(read onl addr: %p\n,p);34 35 printf(coad addr: %p\n,main);36 return 0;37 } 简单介绍 栈又叫堆栈–非静态局部变量/函数参数/返回值等等栈是向下增长的。内存映射段是高效的I/O映射方式用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存做进程间通信。Linux课程如果没学到这块现在只需要了解一下堆用于程序运行时动态内存分配堆是可以上增长的。数据段–存储全局数据和静态数据。代码段–可执行的代码/只读常量。 C语言中动态内存管理方式 开辟内存相关函数malloc,realloc,calloc ✨malloc void* malloc (size_t size); 开辟size字节大小的连续空间开辟成功返回所开辟空间的地址开辟失败返回NULL ✨calloc void* calloc (size_t num, size_t size); 开辟num个size大小字节连续的空间并为所开辟空间中每个字节赋初始值0,开辟成功返回所开辟空间的地址开辟失败返回NULL ✨realloc void* realloc (void* ptr, size_t size); realloc可以用于开辟空间但最重要的是这个函数可以为所开辟空间扩容 realloc为ptr所指向空间扩容将原始空间扩容为size字节扩容成功返回所开辟空间地址开辟失败返回NULL 注意realloc扩容方式有两种 1原地扩容扩容成功返回原来内存空间地址空间地址并不发生变化 2异地扩容扩容成功返回一个全新内存空间的地址空间地址发生变化之间空间的数据拷贝至新的内存空间。 注意realloc也有缩容的作用但是推荐不要使用缩容存在数据丢失的风险。 注意因为realloc开辟空间的方式并不确定处理时要格外小心. 错误用示范
void Test1()
{int* p1 (int*)malloc(sizeof(int)*10);cout p1 endl;p1 (int*)realloc(p1, sizeof(int) * 10000000000);cout p1 endl;cout (void*)NULL endl;free(p1);
}run: 以如上方式用realloc对p1空间进行扩容并发生异地扩容同时扩容失败的情况下用指向旧空间的指针存放新空间的地址会丢失旧空间的数据所以正确使用方式如下 正确示范
void Test1()
{int* p1 (int*)malloc(sizeof(int)*10);cout p1 endl;int* p2 (int*)realloc(p1, sizeof(int) * 1000);if (p2NULL){cout 开辟空间失败 endl;exit(-1);}p1 p2;cout p1 endl;free(p1);}run: 释放内存相关函数free
free void free (void* ptr); 释放ptr指向空间资源 C语言中动态内存管理方式 C语言内存管理方式在C中可以继续使用但有些地方就无能为力而且使用起来比较麻烦因此C又提出了自己的内存管理方式通过new和delete操作符进行动态内存管理。 有痛点才能更加理解C语言中动态内存管理方式的优势。
#includeiostream
using namespace std;
class aim
{
public:aim(int temp):_num(temp){}
private:int _num;
};
int main()
{aim* p1 (aim*)malloc(sizeof(aim));aim* p2 new aim(100);free(p1);delete p2;return 0;
}思考用C的动态内存管理方式你要如何对p1进行管理 以C的动态内存管理方式开辟空间不会执行构造函数对于类的构造函数我们无法显示调用构造函数但是我们可以用定位new的方式解决初始化问题见下文以C的动态内存管理方式释放空间不会调用析构函数清理对象资源但可以显示调用析构函数清理对象资源。 但是用C的动态内存管理方式我们就十分轻松的解决了初始化问题。 new会开辟空间并调用构造函数delete会调用析构函数清理资源并释放空间。 ✨new和delete操作内置类型
void Test()
{// 动态申请一个int类型的空间int* ptr1 new int;// 动态申请一个int类型的空间并初始化为10int* ptr2 new int(10);// 动态申请10个int类型的空间int* ptr3 new int[3];delete ptr1;delete ptr2;delete[] ptr3;//以下方式在C11下支持// 动态申请10个int类型的空间并赋值int* ptr4 new int[10] {1, 2, 3, 4};delete[] ptr4;
} ✨new和delete操作自定义类型
#includeiostream
using namespace std;
class aim
{
public:aim(int temp):_num(temp){cout this endl;}~aim(){cout this endl;}
private:int _num;
};
int main()
{aim* p1 (aim*)malloc(sizeof(aim));aim* p2 new aim(100);cout p1- p1endl;cout p2 -p2 endl;free(p1);delete p2;return 0;
}run 我们可以清晰的看到用C的动态内存管理方式申请/释放空间不会调用构造函数/析构函数。 申请多个内存空间
C11支持用以下方式赋值
aim* p3 new aim[5]{1,2,3,4,5};如果出现报错:无默认构造函数,可用此方式强制生成默认构造函数
aim() default;operator new与operator delete函数 new和delete是用户进行动态内存申请和释放的操作符operator new 和operator delete是系统提供的全局函数new在底层调用operator new全局函数来申请空间delete在底层通过operator delete全局函数来释放空间。 关于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) 通过上述两个全局函数的实现知道operator new 实际也是通过malloc来申请空间如果malloc申请空间成功就直接返回否则执行用户提供的空间不足应对措施如果用户提供该措施就继续申请否则就抛异常。operator delete 最终是通过free来释放空间的。 以汇编语言验证new的底层原理 new和delete的实现原理 ✨内置类型 如果申请的是内置类型的空间new和mallocdelete和free基本类似不同的地方是new/delete申请和释放的是单个元素的空间new[]和delete[]申请的是连续空间而且new在申请空间失败时会抛异常malloc会返回NULL。 ✨自定义类型
new的原理 调用operator new函数申请空间在申请的空间上执行构造函数完成对象的构造 delete的原理 在空间上执行析构函数完成对象中资源的清理工作调用operator delete函数释放对象的空间 new T[N]的原理 调用operator new[]函数在operator new[]中实际调用operator new函数完成N个对 象空间的申请在申请的空间上执行N次构造函数 delete[]的原理 在释放的对象空间上执行N次析构函数完成N个对象中资源的清理调用operator delete[]释放空间实际在operator delete[]中调用operator delete来释 放空间 一些疑问 为什么new T[N]方括号中要填入具体对象个数N而delete[]的方括号中不需要填入具体的对象个数 示例
#includeiostream
#includevector
using namespace std;
int main()
{vectorint* v new vectorint[10];cout V-v endl;delete[] v;return 0;
}我们可以清晰的看到在V的地址之前存储了需要释放空间个数所以delete[]的方括号中不需要填入具体的对象个数开辟的空间逻辑图如下 定位new表达式(placement-new) 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。 使用格式 new (place_address) type或者new (place_address) type(initializer-list) place_address必须是一个指针initializer-list是类型的初始化列表 使用场景定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化所以如果是自定义类型的对象需要使用new的定义表达式进行显示调构造函数进行初始化。
#includeiostream
using namespace std;
class aim
{
public:aim() default;aim(int temp):_num(temp){cout this endl;}~aim(){cout this endl;}
private:int _num;
};
int main()
{// p1现在指向的只不过是与A对象相同大小的一段空间还不能算是一个对象因为构造函数没有执行aim* p1 (aim*)malloc(sizeof(aim));new(p1)aim; // 注意如果A类的构造函数有参数时此处需要传参p1-~aim();//析构函数可以显示调用但是构造函数不可以显示调用free(p1);aim* p2 (aim*)operator new(sizeof(aim));new(p2)aim(10);p2-~aim();operator delete(p2);return 0;
}感谢阅读