动态内存管理

简介: 动态内存管理

💕"痛苦难以避免,而磨难可以选择。"-->村上春树💕

作者:Mylvzi

文章主要内容:动态内存管理

前言:为什么要进行动态内存管理-->之前开辟内存的方式对于内存的使用过于局限;

一.动态内存函数的介绍

所有的与动态内存分配有关的函数都包含于<stdlib.h>这个头文件之中

1.malloc函数-->动态内存开辟函数

作用:在堆区之中申请size字节大小的空间,并返回开辟内存空间的起始地址;

注意:

1.在申请内存的过程中有可能申请失败,如果申请失败,会返回NULL;(一定要进行判断,对空指针的解引用是非法的)

2.malloc的返回值是void*,使用者可以根据自身需求进行强制类型转换;

3.动态开辟的内存并不会自动销毁(还给操作系统),销毁有两种方式,使用free函数释放内存空间,程序退出;

2.free函数-->动态内存释放函数

作用:释放之前已经被动态内存函数(malloc,calloc,realoc)开辟的内存空间,将内存空间还给操作系统

注意:

1.只能释放动态开辟的内存,不能释放在栈区开辟的内存:

2.释放之后,要及时对释放空间的起始地址置于NULL,避免野指针的出现

代码示例:

int main()
{
  int* p = (int*)malloc(40);//在堆区申请40byte空间
  //判断malloc函数是否成功在堆区申请到空间,失败,返回NULL  
  //成功,返回所申请空间的初始地址
  if (p == NULL)
  {
    perror("malloc");//打印错误信息
    return 1;
  }
  //使用完毕,使用free函数对内存空间进行释放(和栈区内存的申请是不同的)
  free(p);
  p = NULL;//及时将释放空间的起始地址置于空指针
  /*free函数只能释放动态开辟的内存空间*/
  //int a = 10;
  //int* ptr = &a;
  //free(ptr);//err
  return 0;
}

3.calloc函数-->作用和malloc函数相同,进行动态内存开辟

作用:向内存申请num个size字节大小的内存空间,并把内存空间的内容初始化为0 (malloc函数并不会进行初始化)

代码示例:

//calloc函数-->void* calloc(size_t num,size_t size)
int main()
{
  //int arr[10];
  int* p = (int*)calloc(10, 4);
  if (p == NULL)
  {
    perror("calloc");
    return 1;
  }
  //打印数据
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", p[i]);//打印10个0
  }
  //使用完毕,释放内存空间
  free(p);
  p = NULL;
  return 0;
}

4.realloc函数-->调整动态内存大小的函数(重要,实现动态内存分配的核心函数)

作用:将ptr所指向的内存空间的大小改为size字节(增容)

但是,堆区的内存空间是有限的,在重新分配内存大小时,可能会出现内存空间不足的情况,导致返回值有三种情况;

1.返回旧空间的起始地址

2.返回新开辟空间的起始地址

3.返回NULL

当返回值不确定时,重新创建一个变量来接受

 

代码示例:

//realloc函数
//void* realloc(void* ptr ,size_t size)
int main()
{
  //动态开辟一块内存空间
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  //赋值
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    p[i] = i + 1;
  }
  //增容
  int* ptr = (int*)realloc(p, 80);//增容为80byte
  if (ptr != NULL)//只要不是NULL,就证明开辟成功
  {
    p = ptr;
    ptr = NULL;
  }
  else
  {
    perror("realloc");
    return 1;
  }
  for (i = 0; i < 20; i++)
  {
    printf("%d ", p[i]);
  }
  //使用完毕
  free(p);
  p = NULL;
  return 0;
}

二.动态内存常见错误

1.对NULL的解引用操作(要检测返回值是否为NULL, 是NULL应该直接退出函数)

int* p = (int*)malloc(40);
  *p = 20;//未检测返回值是否为NULL
  free(p);
  p = NULL;

2.对动态开辟空间的越界访问(不能超过你申请内存空间的大小)

int* p = (int*)malloc(20);//申请了一个能容纳5个int类型数据的空间
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    p[i] = i + 1;//超过内存限制
  }

3. 对非动态开辟内存进行free释放(free只能释放动态开辟的内存)

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

4.使用free释放一块动态开辟内存的一部分 (不能占着茅坑不拉屎)

int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  p++;
  free(p);//ERR p此时已经不再是起始位置,不能释放内存的一部分
  p = NULL;

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

int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  free(p);
  free(p);//err  已经释放过的不能再被释放

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

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

总结:动态内存的开辟虽然方便,但它是把双刃剑,会带来其他危险,在使用过程中要注意规范,养成以下习惯可以规避一些风险

1.在使用动态内存函数开辟完空间后,及时判断是否申请成功(p是否为NULL)

2.要及时释放动态开辟的内存(谨记malloc和free是成对出现的),并在释放后将地址置于NULL,避免野指针的出现;

3.在接受realloc函数的返回值时,最好创建一个新的变量来接收,并判断是否增容成功

补充一点

       free函数的参数中只有一个void* 类型的地址,并未告诉你对应空间的大小,那他是如何精准释放空间呢?原因在于,在malloc函数分配内存时,系统会在你所申请的内存之前(或之后,具体看编译环境)设置一个“头部信息”,free函数在得到起始地址后,会根据头部信息的内容知道内存的具体大小,从而实现对内存的精准释放

三.动态内存分配笔试题讲解

1.题目一:

解决方案:你想改变实参str的值,为实参开辟空间,改变实参,要传递实参的地址!

void GetMemory(char** p)//使用二级指针存放str的地址
{
  *p = (char*)malloc(100);//对p解引用,得到str所在的空间
}                           //为str分配足够的空间
void Test(void)
{
  char* str = NULL;
  GetMemory(&str);
  strcpy(str, "hello world");
  printf(str);//输出hello world
}

2.题目二:

注意:返回栈区空间地址,产生野指针,非法访问内存

3.题目三:

free之后一定要及时将旧地址置为空指针

四.c/c++程序内存分配

五.总结

总结:动态内存分配是一种管理内存的重要方式,要了解与动态内存管理有关的函数(malloc,calloc,realloc,free),熟记动态内存分配过程中的危险(是否分配成功,realloc函数的返回值有三种情况),了解基本的内存分配知识;感谢大家观看

目录
相关文章
|
2月前
|
程序员 编译器 C语言
|
4月前
|
编译器 C语言
动态内存管理(1)
动态内存管理(1)
37 4
|
4月前
|
程序员
21.动态内存管理
21.动态内存管理
|
11月前
|
C语言 Python
动态内存管理(下)
动态内存管理(下)
51 0
|
5月前
|
程序员 编译器 C语言
带你彻头彻尾了解『动态内存管理』
带你彻头彻尾了解『动态内存管理』
|
5月前
|
编译器 程序员 C语言
动态内存管理(超详细!)
动态内存管理(超详细!)
48 2
|
10月前
|
程序员 编译器 C语言
动态内存管理(2)
动态内存管理(2)
45 0
|
10月前
|
程序员 编译器 C语言
动态内存管理总结
动态内存管理总结
52 0
|
11月前
|
C语言 C++
C++中的动态内存管理
C++中的动态内存管理
|
11月前
动态内存管理(下)
动态内存管理(下)
35 0