动态内存管理

本文介绍了动态内存分配的重要性,详细阐述了malloc、calloc、realloc和free等函数的使用,并列举了动态内存使用中常见的错误,如NULL指针解引用、越界访问、非法释放和内存泄漏。同时,通过经典例子分析了动态内存管理的正确做法,强调了内存释放和管理的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:为什么进行动态内存分配

int a=0;          //在栈区开辟四个字节
char ch[20];      // 在栈区开辟20字节

以上内存开辟开辟的空间大小是固定的,但是对于空间的需求,不仅仅是上述的情况。
有时候我们需要的空间大小在程序运行的时候才能知道,这时候就只能试试动态内存开辟了。

在这里插入图片描述
栈区:
普通的局部变量在栈区分配空间的,在上面创建的变量出了作用域就销毁。
堆区:
一般由程序员分配释放, 若程序员不释放,程序结束时可能由操作系统回收 ,上图为一些动态内存函数。
静态区:
被static修饰的变量存放在数据段(静态区),在上面创建的变量,直到程序结束才销毁

二:动态内存函数
1.malloc
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
在这里插入图片描述
成功开辟空间示例:

int*p = (int*)malloc(40);

如果开辟成功,则返回一个指向开辟好空间的指针。
malloc返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,此处将返回的指针强制类型转换成整形指针,具体在使用时根据需求决定转换成哪种类型的指针。

开辟空间失败示例:

int* p = (int*)malloc(1000000000*sizeof(int));  //给的参数过大

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

最后注意,如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

2.free
专门是用来做动态内存的释放和回收的
在这里插入图片描述

void free (void* ptr);

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

3.calloc
calloc 函数也用来动态内存分配
在这里插入图片描述
函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

开辟和释放示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
 int *p = (int*)calloc(10, sizeof(int));
 if(NULL != p)
 {
 //使用空间
 }
 free(p);
 p = NULL;
 return 0;
}

4.realloc
有时我们发现过去申请的空间太小或太大了,为了合理使用内存,应对内存的大小做灵活的调整。 realloc 函数就可以做到对动态开辟内存大小的调整。
在这里插入图片描述

void* realloc (void* ptr, size_t size);

ptr 是要调整的内存地址
size 是调整之后新大小
返回值为调整之后的内存起始位置

使用realloc时有以下两种情况
在这里插入图片描述

情况1— 在原来内存之后直接追加空间,原来空间的数据不发生变化。
情况2 — 原有内存空间之后没有足够多的空间时,在堆上另找一个合适大小的连续空间来使用,这个连续空间的大小为 size,原空间被释放,数据移到新空间 ,函数返回新空间的地址。

realloc也可能找不到合适的空间,此时返回NULL

三:常见的动态内存使用错误
1 对NULL指针的解引用操作

int *p = (int *)malloc(INT_MAX/4);//给的参数太大开辟失败返回NULL
 *p = 20;//对NULL解引用

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

 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释放

 int a = 10;
 int *p = &a;
 free(p);

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

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

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

 int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放

6 动态开辟内存忘记释放

内存泄漏:是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

动态内存使用错误的经典题目:
题目1

void GetMemory(char *p)
{
 p = (char *)malloc(100);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(str);
 strcpy(str, "hello world");
 printf(str);
}

①Test函数调用GetMemory时使用值传递,p只是str的临时拷贝,str的值不会被修改,仍为NULL,此时使用strcpy并打印,不会有内容显示。
②GetMemory函数返回后,临时变量p销毁,动态开辟的空间没有指针指向,也没有释放,造成内存泄漏。

正确写法如下

char* GetMemory(char* p)
{
     p = (char*)malloc(100);
     return p;
}
void Test(void)
{
    char* str = NULL;
    str = GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
    free(str);
    str = NULL;
}

也可以把值传递修改为传引用

void GetMemory(char** p)
{
	*p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str);

	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}

题目2

char *GetMemory(void)
{
   char p[] = "hello world";
   return p;
}
void Test(void)
{
   char *str = NULL;
   str = GetMemory();
   printf(str);
}

GetMemory中局部变量占用的栈空间在调用GetMemory后被释放,返回局部变量的地址没有意义,通过返回的局部变量的地址访问内存是非法的。
示例:
在这里插入图片描述
题目3

void GetMemory(char **p, int num)
{
     *p = (char *)malloc(num);
}
void Test(void)
{
     char *str = NULL;
     GetMemory(&str, 100);
     strcpy(str, "hello");
     printf(str);
}

没有释放动态开辟的空间,正确的如下

     char *str = NULL;
     GetMemory(&str, 100);
     strcpy(str, "hello");
     printf(str);
     free(str);
     str=NULL;

题目4

void Test(void)
{
   char *str = (char *) malloc(100);
   strcpy(str, "hello");
   free(str);
   if(str != NULL)
   {
     strcpy(str, "world");
     printf(str);
   }
}

使用free后,str未置为NULL,str不为空指针,再使用它访问内存是非法的,要记得使用free后置空

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神奇dyl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值