目录
当一个数组被声明时,它所需的内存在编译时才被分配,但是,它的内存空间取决于输入数值。在声明数组时,通常声明一个较大的数组,这种方法的优点是简单,但它也有缺点,在声明数组时,申请内存空间小了,会导致元素数量超过声明长度,申请空间大了,会导致内存空间浪费。因此,引入动态内存管理这个概念十分重要。
一.C/C++程序的内存开辟
C/C++程序内存分配的几个区域:
1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结 束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是 分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返 回地址等。
2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分 配方式类似于链表。
3. 数据段(静态区):(static)存放全局变量、静态数据。程序结束后由系统释放。
static修饰的局部变量:普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。 但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁所以生命周期变长。4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
二.动态内存函数的介绍
1.malloc
函数原型:void * malloc(size_t size); 头文件是stdlib.h
malloc函数的参数是需要分配的内存字节数。 注意:参数的类型是size_t,它是一个无符号类型。
malloc函数的作用是从内存池中提取一块连续的内存空间,该函数的返回值是该内存空间的首地址,而且这块空间是未初始化的。因此,要么手动初始化,要么用calloc函数初始化。当这块空间不再使用时,需用free函数把它归还给内存池。
如果内存池是空的,或者它的可用内存无法满足请求,malloc函数就会返回一个NULL指针。因此,对每个从malloc返回的指针都要进行检查,确保它并非NULL,这一点十分重要。
还需要注意的是malloc并不知道所请求什么类型的数组空间,他返回的值void * 的指针,因此需要对他进行任何类型的转换。
2.free
函数原型:void free ( void * pointer ); 头文件是stdlib.h
free的参数必须要么是NULL,要么是一个先前从malloc、calloc或realloc。返回的值。向free传递一个NULL参数不会产生任何效果。
free函数只是将参数指针指向的内存归还给操作系统,并不会把参数指针置NULL,为了以后访问到被操作系统重新分配后的错误数据,所以在调用free之后,通常需要手动将指针置NULL。
野指针问题:free(str)后,str指针变量仍然指向原来的堆地址,不把它置空,会造成野指针问题,所以最好free()了以后再置空str = NULL
3.calloc
函数原型:void * calloc(size_t num ,size_t size); 头文件是stdlib.h
使用方式与malloc几乎相同,也是在堆区申请动态内存空间,返回类型也为空指针,size_t num为元素个数,size_t size为每个元素的字节大小。
malloc和calloc的区别
1.参数的使用方式不同
malloc(单位:字节):malloc(10 * sizeof(int))或malloc(40)
calloc:calloc(10 , sizeof(int))
2.malloc的使用效率较高,因为calloc在返回在堆区申请的那块动态内存的起始地址之前,会将每个字节都初始化为0
4.realloc
函数原型:void * realloc(void * ptr, size_t size); 头文件是stdlib.h
realloc函数返回的是void*类型的指针,指向在堆区重新开辟的内存块的起始地址,ptr是先前开辟的内存块的指针(malloc或calloc之前申请的那块内存空间),size_t size是调整之后的大小,注意不是增加的字节数,而是新开辟的那块内存空间的字节数。
注意realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间
要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
情况2:原有空间之后没有足够大的空间
原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小 的连续空间来使用。这样函数返回的是一个新的内存地址。
由于上述的两种情况,realloc函数的使用就要注意一些。
三.常见的动态内存错误
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
void test()
{
int a = 10;
int *p = &a;
free(p);//p指向的空间不是堆区里开辟的空间
}
栈区分配的内存是不能用free
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
p不再指向动态内存的起始位置,只释放了起始地址后面的一部分空间
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
同一个地址不能重复释放
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
动态开辟的空间一定要释放,并且正确释放 。