山西建设网站公司,wordpress自适应淘宝客主题,wordpress用户中心api,网上的网站模板怎么用C/C内存管理C/C内存分布C语言中内存管理的方式#xff1a;malloc/calloc/realloc/freeC内存管理方式内置类型自定义类型operator new 与operator deletenew和delete的实现原理内置类型自定义类型定位new表达式(placement-new)new/delete与malloc/free的区别C/C内存分布
我们先…
C/C内存管理C/C内存分布C语言中内存管理的方式malloc/calloc/realloc/freeC内存管理方式内置类型自定义类型operator new 与operator deletenew和delete的实现原理内置类型自定义类型定位new表达式(placement-new)new/delete与malloc/free的区别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);
}
1. 选择题选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)globalVar在哪里____ staticGlobalVar在哪里____staticVar在哪里____ localVar在哪里____num1 在哪里____char2在哪里____ *char2在哪里___pChar3在哪里____ *pChar3在哪里____ptr1在哪里____ *ptr1在哪里____
2. 填空题sizeof(num1) ____;sizeof(char2) ____; strlen(char2) ____;sizeof(pChar3) ____; strlen(pChar3) ____;sizeof(ptr1) ____;
3. sizeof 和 strlen 区别首先我们来回答第一题 globalVar属于全局变量全局变量存储与全局区或静态区所以答案选C staticGlobalVar,static修饰的全局变量存储与静态区所以答案选C staticVarstatic修饰的局部变量也属于静态变量也存储与静态区所以答案选C localVar属于main函数内部的局部变量存储在main函数的栈帧中所以答案选A num1 ,数组名num1整个数组是开辟在main函数的栈帧上的属于局部变量也是存储与栈上所以答案选A char2在main函数栈帧上开辟随着main函数栈帧的销毁而销毁属于局部变量存储在栈上所以选A *char2char2没有与结合也没有单独放在sizeof内部char2表述数组首元素地址对其解引用访问到的是数组首元素整个数组都是开辟在栈上的首元素当然也不例外故答案选A pChar3在栈上开辟选A *pChar3“abcd是存储与常量区的故pChar3存储的是常量区的地址对其解引用访问到的就是首元素整个字符串都是存储与常量区的首元素也不例外故选D ptr1局部变量存储与栈上故答案选A *ptr1,是用malloc开辟的空间malloc是从堆区申请空间ptr1是堆区的地址对其解引用就是堆区的空间故选B 第二题 num1是数组名数组名单独放在sizeof内部表示整个数组故求得大小为40字节 char2是数组名数组名单独放在sizeof内部表示整个数组故求得大小是5字节不要忘了’\0’; strlen(char2)strlen遇到’\0’停止故求得大小为4 sizeof(pChar3)pChar3是指针大小为4/8字节 strlen(pChar3)strlen遇到’\0’停止故求得大小为4 sizeof(ptr1)ptr1是指针故求得大小为4/8字节 第三题 sizeof是求数据所占内存空间大小的关键字strlen是求字符串长度的函数 总结 栈又叫堆栈–非静态局部变量/函数参数/返回值等等栈是向下增长的。内存映射段是高效的I/O映射方式用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存做进程间通信。Linux课程如果没学到这块现在只需要了解一下堆用于程序运行时动态内存分配堆是可以上增长的。数据段–存储全局数据和静态数据。代码段–可执行的代码/只读常量。 C语言中内存管理的方式malloc/calloc/realloc/free
void test()
{int* tmp (int*)malloc(sizeof(int) * 10);//利用malloc向堆区申请10个int类型的空间这些空间不会被初始化if (tmp nullptr){perror(malloc);exit(EXIT_FAILURE);}int* tmp2 (int*)calloc(sizeof(int), 10);//利用calloc向堆区申请10个int类型的空间编译器会自动以0初始化这些空间if (tmp2 nullptr){perror(calloc);exit(EXIT_FAILURE);}int* p nullptr;int* tmp4 (int*)realloc(p,sizeof(int)*20);//对空间进行扩容当tmpnullptr时realloc相当于mallocif (tmp4 nullptr){perror(calloc);exit(EXIT_FAILURE);}free(tmp4);free(tmp2);free(tmp);
}我们可以发现C语言实现内存管理的方式是通过4个函数这些函数在使用起来似乎并不是那么方便比如不管我们用那种方式(malloc、calloc、realloc)开辟空间我们在使用这些空间之前都必须对其进行判空处理避免后续对空指针解引用的处理如果我们需要频繁的申请空间那么这些判空代码就会被我们大量的使用会增加重复的代码要是每次返回的都是经过malloc等函数验证过的指针该多好用起来也方便还有就是malloc、calloc、realloc参数设计的不太同一使用起来比较复杂 最后一点就是对于所开空间的大小我们都需要手动计算容易计算错误等等简而言之在C语言中实现内存的管理并不是一件易事那么C作为C的扩展在内存管理方面会不会有优化呢
C内存管理方式
内置类型
在C中通过new和delete两个操作符来实现内存管理 比如
void test2()
{//开一个int空间int* p1 new int;//开辟10个连续 int空间int* parr new int[10];//释放空间delete p1;//释放连续空间delete[]parr;
}通过对比C语言的内存管理方式我们可以发现在C实现内存管理的方式非常简洁同时也没有了繁琐的判空的步骤 在C语言中我们从堆上开辟的空间是不能初始化的如果能的话那也就是calloc但是它是只能初始化为0在某些场景下与没初始化没什么区别 在C中我们可以指定值来初始化我们从堆区开辟的空间 比如 调试结果 总结 1、开辟单块空间: new type (初始值) 开辟连续空间: new type[大小] {初始值1,初始值2,……} 释放单个空间delete 指针 释放连续空间delete [ ] 指针 千万要注意new和delete配套使用new [] 与delete [ ]配套使用如果乱使用的话可能会引发一些不可预料的后果 2、new出来的空间不需要进行强转和判空 自定义类型
上面是对于内置类型的内存管理那么对于自定义类型呢 其基本用法与内置类下的用法大致一样但是还是有一点区别
class A
{
public:A() :_a(0){cout A() endl;}A(int a) :_a(a){cout A(int a) endl;}A(const A a) :_a(a._a){cout A(const A a) endl;}~A(){cout ~A() endl;}void test(){cout void test() endl;}A operator(const A a){cout A operator(const A a) endl;_a a._a;return *this;}
private:int _a;
};
void test3()
{//向堆区开辟一块空间A* p new A;//向堆区开辟一块连续的空间A* parr new A[5];delete p;delete[] parr;
}运行结果 我们可以发现自定义类型在利用new向堆区申请空间时会自动调用构造函数来初始化空间在利用delete释放空间的时候会自动调用析构函数 通过实验结论可以看出当我们不指定特定值来初始化时编译器默认调用默认构造函数来初始化 那么我们如何让对象按照我们指定的构造函数来初始化空间呢 用法如下 总结 1、利用new给自定义类型开辟空间时如果不指定构造函数则编译器会使用默认构造函数来初始化delete空间时编译器会调用先调用析构函数而对于malloc/free函数来说并不会调用构造函数和析构函数 2、对于自定义类型我们也可以指定构造方式来初始化 operator new 与operator delete
new与delete是用户在C中进行内存管理的操作符其底层是用operator new和operator delete这两个全局函数来实现的是的你没听错operator new和operator delete是全局函数不是运算符重载operator new和operator delete就是函数名与Add、Show、test等一样的函数名这个名字很有误导性我们需要注意 既然new和delete底层是用operator new 与operator delete两个全局函数实现的那么我们来看看这两个函数的具体实现 operator new
//operator new该函数实际通过malloc来申请空间当malloc申请空间成功时直接返回申请空间
失败尝试执行空间不足应对措施如果改应对措施用户设置了则继续申请否则抛异常。
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// 如果申请内存失败了这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}operator new参数和返回值与malloc一致 我们可以发现operator new函数内部也是调用malloc来实现的只不过operator new对malloc进行了封装就是说operator new函数对于malloc 开辟空间失败 的做法进行了优化不再是返回空指针而是直接抛出异常而对于开辟成功的空间则是直接返回所开空间的首地址这对于我们使用者来说方便了不少我们无需在每次对从堆区申请的空间进行判空了可以大胆放心的使用但是operator new函数的返值是void*也就是说我们还得需要对指针进行强转 operator delete:
//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都有了那么自然的作为“开辟”的伴随者“释放”自然也就少不了operator delete 就是对free的封装用法也与free一样
我们可以在我们的程序中直接使用这些函数
new和delete的实现原理
内置类型
对于内置类型来说new、delete与malloc、free基本类似 不同的是:接受new出来的空间不需要进行强制类型转换malloc的返回值需要进行强制类型转换 还有就是new开辟空间失败会抛出异常不会返回空指针而malloc开辟空间失败是会返回空指针的 new出来的空间可以放心使用malloc出来的空间在使用前需要判断是否是合法空间判空
自定义类型 new原理 1、调用operator new函数开辟空间 2、调用构造函数初始化空间 delete原理 1、调用析构函数进行资源清理 2、调用operator delete函数进行对象本身的释放 new [N] 1、调用operator new[](也就是对于operator new的封装目的是为了对应new [ ]),operator new[]实际通过调用operator new来完成的函数开辟N个连续空间的对象 2、调用N次构造函数分别对每个对象进行初始化 delete [ ] 1、调用N次析构函数分别对每个对象进行资源清理 2、调用operator delete[](也就是对于operator delete的封装目的是为了和operator new配对)实际是通过调用operator delete来完成对N个连续对象本身的释放 在这里我再次强调一下 malloc与free匹配使用 new与delete匹配使用 new[]与delete[]匹配使用 千万不要混用不然会出现不可预知的后果 比如
我们new出来的空间利用free来释放调用free来释放的话编译器是不会调用对象的析构函数的对于本例来说并没有多大影响因为A类没有额外申请资源不需要对资源进行手动释放但是如果是栈类呢 虽然我们完成了Stack栈类对象本身空间的释放但是我们造成了内存泄漏_a所指向的空间也是我们从堆上开辟的我们利用free释放就不会调用析构函数也就无法完成_a所指向的空间的释放 当我们使用delete时编译器才会调用析构函数完成_a所指空间的释放 这就是乱用的后果内存泄漏是个很严重的问题在C/C语言中编译器是不会检查内存泄漏的 还有一个例子也是乱使用delete、free、delete[]造成的程序崩溃 我们可以看到使用free和delete释放new[]开辟的空间时程序直接崩溃了 这是为什么 我们刚才说了delete []释放空间的时候是需要调用N次析构函数的对于new[N]需要调用N次构造函数new很容易知道但是delete怎么知道他要调用N次析构函数呢主要是因为为new[]在开辟空间的时候在所开空间的前面多开了几块空间这多开的空间就是专门用来存储delete[]该调用几次析构函数的 为此我们在使用delete[]的时候operator delete[]会先将指针往前偏一点拿到调用析构函数的次数然后调用N次析构函数最后在从多开出的空间开始释放而不是从new[]返回的地址开始释放当我们使用free、delete的时候都是从new[]返回的地址处开始释放的会造成红色部分空间没有得到释放系统就会崩溃 当然如果我们将析构函数注释掉那么free、delete、delete[]三种方式释放空间编译器都不会报错了这是为什么 编译器也是很聪明当编译器发现你是使用的默认析构函数也就是编译器自动生成的时候他就会认为此时对new[]出来的空间析不构析构好像没多大意义了那么在new[]的时候也就不会多开辟空间出来存储需要delete[]需要调用析构函数的次数那么自然delete、free就不会造成空间的少释放 总而言之上面的例子都在告诉我们要匹配malloc/free、new/delete、new[]/delete[]使用
定位new表达式(placement-new)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。 使用格式 new (place_address) type或者new (place_address) type(initializer-list) place_address必须是一个指针initializer-list是类型的初始化列表 使用场景 定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化所以如果是自定义类型的对象需要使用new的定义表达式进行显示调构造函数进行初始化。 这里简单介绍一下内存池内存池故名思意就是一个池子里面装的都是内存块我们在使用new、malloc等操作符或函数申请空间的时候都是直接去os管理的堆上开辟的空间所有需要申请空间的程序都是从这里申请的在这里申请空间就避免不了与os的交互交互是需要花费时间的如果一个程序中需要频繁的开辟空间那么程序会在申请空间的路上花费大量时间时间效率有点低因为这需要频繁的与os交互为此大佬们提出了内存池的技术就是预先从os管理的堆上开辟一部分空间出来程序想要申请空间的时候就不需要向os申请了直接去内存池拿减少了os的交互时间提高了程序运行效率如果当内存池的空间不足时再由内存池向os申请一块更大的空间过来 new/delete与malloc/free的区别 共同点 无论是new还是malloc都是从堆上申请空间都需要用户自己手动对这些空间进行手动释放 不同点 1、new的返回值是不需要进行强制转换的malloc需要对返回类型进行强转 2、对于new开辟的空间我们不需要进行判空new如果开辟失败的话或抛出异常malloc开辟失败会返回空指针因此我们在使用malloc开辟空间时需要自己手动判断空间是否开辟成功 3、new空间的时候只需要告诉我们想要开辟空间的个数malloc开辟空间的时候需要我们手动计算所开空间的大小 4、new可以用指定值初始化开辟出来的空间malloc不能对开辟出来的空间进行初始化 5、对于自定义类型来说new在开辟好空间后会调用其构造函数来初始化这块空间delete释放这块空间的时候会先调用该对象的析构函数来清理该对象的资源然后才完成对象本身的释放malloc只会开辟空间不会调用构造函数free也只是释放空间不会调用析构函数 6、new/delete是操作符malloc/free是函数