前言:
什么是动态内存分配?
所谓动态内存分配,就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。 动态内存的好处是归纳为8个字:用时分配,不用释放!”
堆和栈有什么区别?
栈区内存由系统分配和释放;堆区内存由程序员掌控。
程序启动时会为栈区分配一块大小适当的内存,再向这块已有内存中写入数据时,不会涉及内存的分配和释放,所以栈内存的分配效率要高于堆。
1.动态内存分配函数的介绍及注意事项:
malloc()
原型:void* malloc (size_t size);
作用:在堆区分配 size 字节的内存空间。
返回值:成功返回分配的内存地址,失败则返回NULL。
注意:分配内存在动态存储区(堆区),手动分配,手动释放,申请时空间可能有也可能没有,需要自行判断,由于返回的是void*,需要手动强制类型转换。
calloc()
原型:void* calloc(size_t n, size_t size);
功能:在堆区分配 n*size 字节的连续空间。
返回值:成功返回分配的内存地址,失败则返回NULL。
注意:第一参数是第二参数的单元个数,第二参数是单位的字节数。
realloc()
原型:void* realloc(void *ptr, size_t size);
功能:对 ptr 指向的内存重新分配 size 大小的空间,size 可比原来的大或者小,还可以不变。
返回值:成功返回更改后的内存地址,失败则返回NULL。
free()
原型:void free(void* ptr);
功能:释放由 malloc()、calloc()、realloc() 申请的内存空间。
注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
每个内存分配函数必须有free函数,释放后不能再次使用被释放的内存。
分配内存最好使用sizeof,用来判断数据类型长度的单目操作符。因为程序移植时,不同的操作系统,同一数据类型的长度可能不一样。
free()函数不能改变指针的值,所以当指针初始化时,尽量声明为NULL。
2.动态内存分配函数的使用
1.malloc
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(sizeof(int) * 4);
}
在这里我们在堆上开辟了4个整型的空间,由于malloc这个函数返回的是void*所以我们需要将其强转为int*。
如何操作这4个整型的空间了
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(sizeof(int) * 4);
if (p != NULL)//防止动态内存分配失败
{
for (int i = 0; i < 4; i++)
{
p[i] = i;
}
for (int j = 0; j < 4; j++)
{
printf("%d ", p[j]);
}
}
return 0;
}
运行结果:
操作的方法和数组类似
但特别要注意的是要对malloc返回的指针进行检查,确保他不是空指针
2.calloc 这个函数的使用方法类似
我们可以看看他的函数原型
void* calloc(size_t n, size_t size)
第一个参数是个数,第二个参数每个元素所占的大小。注意calloc会将开辟好的空间赋初始值0
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)calloc(4, sizeof(int));
if (p != NULL)//防止动态内存分配失败
{
for (int i = 0; i < 4; i++)
{
printf("%d ", p[i]);//打印p所指向空间的内容
}
}
free(p);
return 0;
}
运行结果:
其他用法与malloc基本相同
3.realloc用于调整调整分配的空间
#include<iostream>
using namespace std;
#include<stdlib.h>
#include<string>
int main()
{
int* p = (int*)malloc(sizeof(int));
int* str =(int* ) realloc(p, 2 * sizeof(int));if (str != NULL)
{
p = str;
for (int i = 0; i < 2; i++)
{
p[i] = i;
}
for (int i = 0; i < 2; i++)
{
cout << p[i] << endl;
}free(str);
}return 0;
}
运行结果:
free用于释放动态内存开辟的空间,如果传给free的是空指针,那么free将什么也不做。
总结:
free的参数要么是NULL,要么是一个先前从malloc/calloc/realloc返回的值。向free传递一个NULL参数不会产生任何影响。
这些函数维护一个可用内存池,当一个程序另外需要一些内存时,就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。这块内存此时并没有以任何方式进行初始化。如果对这块内存进行初始化非常重要,要么自己动手初始化,要么使用calloc函数。
常见的动态内存分配错误:
1.对空指针解引用
2.对分配内存进行操作时越界
3。释放并非动态内存分配内存
4. 试图释放动态内存分配的一部分
5.一块动态内存已经释放了仍然使用
野指针:
前面提到,指针消亡,并不意味着其所指向的内存会被自动释放,同样,释放动态内存,并不意味着指针会消亡,也不意味着指针的值会改变
//假设p是代码块内声明的局部指针变量,在代码块执行完毕退出时,p自动消亡,但p指向的大小为8、类型为int的内存空间不会释放掉。 //代码退出后,程序无法再通过指针p释放所申请的动态内存,这块内存已经“泄露——————内存泄漏 int *p=(int*)malloc(sizeof(int));... //运行以后,指针p非但不会消亡,其值还会保持不变,并不会变为null。此时使用if(指针!=null)进行处理也无法起到防错作用。
解决方法:指针被free或者delete后,一定要置为null,没有置为null的指针常称为”野指针“。
下列两种情况也可以称为**”野指针“**:
1)未初始化指针
任何指针变量刚创建时,其内容是随机的,在内存中乱指一通。因此,使用指针前,一定要对其初始化。在声明的同时初始化或赋值,使其指向合法的内存。对于无处可指的指针变量,也要将其赋值/初始化为null,也就是空指针。
2)指向临时变量的指针
当前代码块执行完毕后,代码块中声明的临时变量(包括函数调用时的参数)都自动消亡,其占用的内存空间(栈内存)也被内存管理器收回。
此时,指向这些临时变量的指针便没有意义,使用这些指针会给程序造成不可预料的结果。