动态内存管理

来源:互联网 发布:广场舞真皮舞鞋淘宝网 编辑:程序博客网 时间:2024/05/16 15:21

C: 动态内存开辟在堆上开辟需要手动释放

主要函数有:

void* malloc(size_t size);size为申请空间字节数大小,需要判空,不为空,返回空间地址,否则NULL;

void* realloc(void* ptr, size_t size);  ptr:指向先前开辟的内存块,如果为空,那么作用相当于malloc;size实际上是newsize,有以下情况:

1.0 "newsize<= oldsize"

2.0 "newsize>oldsize"  情况2又有:(1)大一点: 返回原空间地址(2)大很多: 返回新地址


在连续开辟时候需要知道待开辟的位置是否已经被开辟所以动态内存开辟不仅仅只是开辟所要的大小

#include<stdio.h>#include<windows.h>int main(){int* p = malloc(sizeof(int)* 5);printf("%p", p);system("pause");return 0;}


                 

在开辟的空间上实际多了8个字节用来标志已经被使用,以及防止越界。

3.0 void* calloc(size_t  num,size_t size

num:元素个数,size:单个元素大小。它还开辟的空间初始化为0.

对于malloc开辟的空间free时常有以下问题:

(1.0) 释放空指针。(2.0)多次释放。(3.0) 释放对象错误:

#include<stdio.h>#include<windows.h>int main(){int* p = malloc(sizeof(int)* 5);int* p1 = p;//free(NULL);free(p1);/*free(p);free(p);*///printf("%p", p);system("pause");return 0;}

注意:

1.0:The free function causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation. If ptr is a null pointer, no action occurs.如果是空指针,则什么行为都不会发生

2.0:第一次free一个指针的时候,只是清空该指针所指的堆中的对应空间,但该指针变量在栈中的值并没有没清,它还是指向原来分配的内存空间(但是该内存空间已经不属于该指针了,CPU随时可把该指针原来所指的空间分配给任何一个指针变量)。这时,再free一次时,由于该指针已经没有堆空间与之对应了,所以编译器将会提示出错。

3.0 实际上就是free了待free的拷贝真正的对象未被释放。

为了避免它成为一个垃圾指针(野指针);会把该指针赋空。注意malloc和free的匹配使用。

C++里的动态内存开辟

1.0在栈上开辟,不用手动释放。注意匹配使用。

函数原型void* operator new(size_t size).   size申请空间字节数。

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;int main(){int* p1 = new int;//开辟单个空间int*p2 = new int(0);//开辟空间同时进行初始化int* p3 = new int[10];//开辟连续空间delete p1;delete p2;delete[] p3;system("pause");return 0;}


delete在使用时先调的是析构函数,再调operator  delete,在operator delete内部使用了free来释放空间。

new T

T有两种类型:1.0 内置类型,(使用时不调用构造函数)2.0 自定义类型,(1.0 未给出构造函数,编译器未合成默认构造函数时不调用构造函数。2.0显式给出构造函数时调用)

#define _CRT_SECURE_NO_WARNINGS 1#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>#include<new.h>using namespace std;class Test{public:/*void* operator new(size_t size){void* p=malloc(size);return p;}*/private:};void* operator new(size_t size){void* p = malloc(size);return p;}int main(){Test* p = new Test;system("pause");return 0;}
给operator new写了2个函数分别在类成员函数里,全局域内。new Test时调用顺序:1.0类成员函数。2.0 全局函数。 3.0 库函数里的operator new


delete T

T有两种类型:1.0 内置类型,(使用时不调用析构函数)2.0 自定义类型,(1.0 未给出析构函数,编译器未合成默认析构函数时不调用析构函数。2.0显式给出析构函数时调用)


new[]:调用过程;1.operator new[] () 2.operator new()  3.0 malloc()  4.0 构造函数

#define _CRT_SECURE_NO_WARNINGS 1#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>#include<new.h>using namespace std;class Array{public:Array(size_t size = 10): _size(size), _a(0){cout << "Array(size_t size)" << endl;if (_size > 0){_a = new int[size];}}~Array(){cout << "~Array()" << endl;if (_a){delete[] _a;_a = 0;_size = 0;}}private:int* _a;size_t _size;};void Test(){/*int* p1 = new int;delete p1;Array* p2 = new Array;delete p2;int* p3 = new int[10];delete p3;*/Array* p4 = new Array[10];delete[] p4;}int main(){Test();system("pause");return 0;}

结果如下:



分析delete[] 的对象是自定义类型时,是怎么知道要调用 N次析构函数的

#define _CRT_SECURE_NO_WARNINGS 1#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>#include<new.h>using namespace std;class Array{public:Array(size_t size = 10): _size(size), _a(0){cout << "Array(size_t size)" << endl;if (_size > 0){_a = new int[size];}}~Array(){cout << "~Array()" << endl;if (_a){delete[] _a;_a = 0;_size = 0;}}private:int* _a;size_t _size;};void Test(){Array* p4 = new Array[10]; cout<<&p4<< endl;delete[] p4;}int main(){Test();system("pause");return 0;}

结果如下:                                                                                              内存如下:

                                                                    

           

实际申请空间时在空间前面还申请了4个字节来保存元素个数,所以编译器知道new多少个类型空间(调多少次构造函数),也知道delete多大的空间,调多少次析构函数。



 没有析构函数时候的情况:

1.0 
 new[]申请的空间可以大于数组实际所需空间,以记录数组长度,但在缺少有效析构函数的情况下delete仅仅将这一块连续的内存空间释放就可以了,不必再逆序调用析构函数。所以无需记录数组长度,这时new[]申请的空间可以等于数组实际所需的空间。

简单说就是自身及其非静态成员都必须没有定义或删除了析构函数。申请这样对象的数组时一般不会使用额外空间去记录其长度

2.0

调析构函数只是为了清除一些资源这时候就需要知道调用了析构函数多少次以便每个对象内的资源都情况,所以有析构函数时多开辟的四个字节(VS下)是为了保存对象个数。\


delete[]


分析三种情况:

1.0 正常运行,内置类型

2.0 内存泄漏,new[]和free匹配使用。

3.0程序崩溃,new[]和delete使用,未清空完。


定位new表达式:定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。



总结:

1.0 C语言中使用malloc/calloc/realloc/free进行动态内存管理,malloc/calloc/realloc
用来在堆上开辟空间,free将申请的空间释放掉。

2.0 注意:堆上的内存需要用户自己来管理,动态malloc/calloc/realloc的空间,必须free掉,否则会
造成内存泄露;栈上空间具有函数作用域,在函数结束后系统自动回收,不用
用户管理。

3.0 使用_alloc在栈上动态开辟内存,栈上开辟的内存由编译器自动维护,不需要用户显式释放

4.0 (1. 栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。
          2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建
         共享共享内存,做进程间通信。
         3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
         4. 数据段--存储全局数据和静态数据。
         5. 代码段--可执行的代码/只读常量。

       )

5.0【malloc/free和new/delete的区别和联系】
       ( 1. 它们都是动态管理内存的入口。
            2. malloc/free是C/C++标准库的函数,new/delete是C++操作符。
            3. malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和
           析构函数进行初始化与清理(清理成员)。
           4. malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回
           对应类型的指针。

       )

6.0 【new作用】
        调用operator new分配空间。
        调用构造函数初始化对象。
      【delete作用】
       调用析构函数清理对象
       调用operator delete释放空间
      【new[]作用】
       调用operator new分配空间。
       调用N次构造函数分别初始化每个对象。

7.0 定位new表达式(replacement版本)
          定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
          new (place_address) type
          new (place_address) type(initializer-list)
          place_address必须是一个指针,initializer-list是类型的初始化列表。

原创粉丝点击