【C语言】初学者眼中的动态内存管理

文章详细介绍了C/C++编程中的内存管理,包括栈区、堆区、数据段和代码段的内存分配。动态内存函数malloc、calloc、realloc和free的使用方法和注意事项被阐述,同时列举并解析了动态内存管理中常见的错误,如NULL指针解引用、越界访问、错误释放和内存泄漏等问题。
摘要由CSDN通过智能技术生成

目录

一.C/C++程序的内存开辟

二.动态内存函数的介绍

1.malloc

2.free

3.calloc

4.realloc

三.常见的动态内存错误

1.对NULL指针的解引用操作

2.对动态开辟空间的越界访问

3.对非动态开辟内存使用free释放

4.使用free释放一块动态开辟内存的一部分

5.对同一块动态内存多次释放

6.动态开辟内存忘记释放(内存泄漏)


当一个数组被声明时,它所需的内存在编译时才被分配,但是,它的内存空间取决于输入数值。在声明数组时,通常声明一个较大的数组,这种方法的优点是简单,但它也有缺点,在声明数组时,申请内存空间小了,会导致元素数量超过声明长度,申请空间大了,会导致内存空间浪费。因此,引入动态内存管理这个概念十分重要。

一.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函数的使用就要注意一些。

三.常见的动态内存错误

1.对NULL指针的解引用操作

void test()
{
 int *p = (int *)malloc(INT_MAX/4);
 *p = 20;//如果p的值是NULL,就会有问题
 free(p);
}

2.对动态开辟空间的越界访问

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);
}

3.对非动态开辟内存使用free释放

void test()
{
 int a = 10;
 int *p = &a;
 free(p);//p指向的空间不是堆区里开辟的空间
}

栈区分配的内存是不能用free

4.使用free释放一块动态开辟内存的一部分

void test()
{
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置
}

p不再指向动态内存的起始位置,只释放了起始地址后面的一部分空间

5.对同一块动态内存多次释放

void test()
{
 int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放
}

同一个地址不能重复释放

6.动态开辟内存忘记释放(内存泄漏)

void test()
{
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
}
int main()
{
 test();
 while(1);
}

动态开辟的空间一定要释放,并且正确释放 。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值