动态内存开辟(malloc、free等函数)及常见的动态内存错误。


前言

在计算机系统中,我们的内存主要有多个区域。
1.静态储存区——其中主要储存着一些常量,全局变量,static变量等。
2.栈区——主要储存一些局部变量往往都存储在栈区,在函数执行结束之后这些变量会被自动释放。
3.堆区——这是我们文章所讲的动态内存分配申请的位置一般都是以申请创建空间malloc函数及free函数搭配而存在。

一、动态内存函数

1.1 malloc

//size申请的空间是以字节为单位的
void* malloc(size_t size);

  • 函数向内存申请了一块size长度连续的空间。
  • 开辟成功则返回该空间的首元素地址。
  • 开辟失败返回空指针NULL。
  • malloc函数开辟的内存空间初始值为随机值。
  • 注:size不可以为0,malloc的行为是标准并未定义的,取决于不同的编译器。

1.2 free

//永远与开辟函数成对存在
void free(void* ptr);

例: free(ptr);
ptr=NULL;

  • 释放刚刚开辟的动态内存。
  • 仅仅适用于动态开辟于堆上的内存。

1.3 calloc

void* calloc (size_t num, size_t size);

  • calloc功能于malloc类似都是向内存申请空间。
  • calloc申请的空间会自定义初始值为0。

1.4 realloc

在进行程序编写时我能总是需要考虑程序的可读性,内存空间的大小占用,上述malloc于calloc二函数很好的进行的空间的开辟,但是开辟多打,怎样动态开辟才是我们更多要去注意的事情,而realloc函数很好的解决了这个问题。

void* realloc (void* ptr, size_t size);
//若第一个参数为空,则realloc与malloc功能一致

  • ptr 是我们要调整的内存初始地址。

  • size 是我们目标调整的大小。

  • 不过realloc函数在调整时一般有两种可能:

     情况一:若要扩大开辟的内存空间,在原有的空间后面有足够多的空间,则会原地开辟,原有空间的数据不会产生变化。
     情况二:当原有空间后没有足够空间时,在堆空间上另找一个合适大小的空间来开辟,并返回新的内存地址。
    

二、常见动态内存错误

2.1 对NULL指针的解引用操作

int * p=(int *)malloc(20);
if(p==NULL);

当这种情况时不能直接对*p赋值

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

//这条语句代表开辟了20个字节的空间 
int * p=(int *)malloc(20);
//注意 对他进行赋值
//因为仅仅开辟的20个字节的空间,但是定义类型又强制转换成(int *)所以如果直接赋值就会发生越界访问
正确的做法是将i的上线值改为5
//for(int i=0;i<20;i++)
//{
//	*(p+1)=i;
//}
//正确做法
for(int i=0;i<5;i++)
{
	*(p+1=i;
}

2.3 对非动态内存使用free释放

free仅仅适合于堆区malloc、calloc、realloc开辟的数据
例:
int main()
{
	int num=10;
	int *p=&num;
	free(p);
	p=NULL;
	return 0;
}

该语句对非动态内存进行的释放

2.4 在释放时仅仅释放一部分内存的情况

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

这样会造成开辟的动态内存不完全释放,此时p指向的并非刚开始创建时的地址,长期以往会造成内存泄漏。

2.5 多次释放同一空间

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

多次释放编译器会报错。

2.6 忘记释放动态内存造成(内存泄漏)

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

注:开辟空间与释放必须成对存在。
当我们开辟空间后未在原地使用而被调用,或者我们定义动态内存后别人拿去用,此时就容易造成忘记释放空间。
所以当我们定义包含开辟动态内存的函数时要记得注释。
长时间的不释放空间,最后都会导致崩溃。

2.7 总结

在动态开辟空间这个模块要注意。

  1. 只有真正属于自己定义的空间,才可以随意的调用使用。
  2. 在函数中虽然对内容进行的更改,但是出函数后都要注意销毁以及是否局部变量直接销毁。

三、几个经典的笔试题(私聊、评论获取解析)

一键三连,评论私聊,在线即回
1.

//求test运行结果
void GetMemory(char *p)
{
 p = (char *)malloc(100);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(str);
 strcpy(str, "hello world");
 printf(str);
}
//求test运行结果
char *GetMemory(void)
{
 char p[] = "hello world";
 return p;
}
void Test(void)
{
 char *str = NULL;
 str = GetMemory();
 printf(str);
}
//求test运行结果
void GetMemory(char **p, int num)
{
 *p = (char *)malloc(num);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, "hello");
 printf(str);
}
//求test运行结果
void Test(void)
{
 char *str = (char *) malloc(100);
 strcpy(str, "hello");
 free(str);
 if(str != NULL)
 {
 strcpy(str, "world");
 printf(str);
 }
}
  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值