C语言中灵活多变的动态内存,malloc函数 && free函数&& calloc函数 && realloc函数

简介: C语言中灵活多变的动态内存,malloc函数 && free函数&& calloc函数 && realloc函数

🚀前言

铁子们好啊!今天阿辉给大家讲一下C语言里面特别重要的一块知识——动态内存管理,为什么说它重要呢?相信大家在使用数组时都遇到过数字开辟空间太大或者太小的情况,这是很尴尬的因为数组空间一旦确定就难以扩大或者缩小。而今天的动态内存管理将帮我们很好的解决这一窘境。话不多说,开始咱们今天的学习吧!!!

🚀管理动态内存的函数

动态内存的管理其实也就分为两块

  • 空间的开辟和扩容
  • 空间的释放

而要做到以上两点,我们要引入几个重要的管理动态内存的函数

动态内存的开辟和释放都是在堆区上

✈️malloc函数

malloc函数是用来开辟空间的,如何使用呢?下面是它的声明👇

void* malloc(size_t size);

关于malloc函数的注意事项:

  • size表示malloc函数所开辟空间的大小,单位是字节
  • maloc函数对于开辟空间存放何种类型的数据并不关心,返回值是void*类型,malloc开辟空间成功,则返回这块空间的起始位置的地址;若开辟空间失败,则返回空指针NULL
  • malloc传入的参数为0时,这种行为是C语言标准未定义的,取决于编译器
  • malloc函数开辟的空间,里面的内容是随机的

具体使用:

int mian()
{
  int* p = (int*)malloc(sizeof(int) * 10);//开辟10*4个字节的空间,返回值强转为int*
  if (p == NULL)//判断动态内存是否开辟成功
  {
    printf("malloc 开辟空间失败\n");//开辟失败返回调试语句,就是让程序员知道哪错了
    return 1;//开辟失败直接返回
  }
  //对动态开辟内存的进一步使用
  //这玩意其实就是个数组,有了首元素地址,不就是当成数组来玩嘛
  for (int i = 0; i < 10; i++)//十个元素初始化成0~9
  {
    p[i] = i;//这玩意与*(p+i) = i等价,不懂得可以看阿辉之前关于指针的文章
  }
  return 0;
}

关于malloc函数开辟的空间,它是一块连续的空间,直接当成数组使用一点毛病没有,

它与数组最大的区别在于,malloc开辟的空间是在堆区上的,而数组是在栈区上的。

而堆区上申请的空间须有程序员自己手动释放(Java,Python有垃圾回收机制),否则将造成内存泄漏。其实就是你从我这拿了空间去用,不用了就还给我,别占着茅坑不拉屎,你不用别人也用不了。对于堆区申请的空间的释放,这里要引入下面这个新的函数

✈️free函数

C语言提供了一个函数free,专门用来做动态内存的释放和回收的,函数声明如下:

void free(void* ptr);

对于free函数使用时的注意事项:

  • 对于传给free的参数必须是指向动态开辟内存(也就是是堆区开辟的空间)的起始地址的指针
  • 若传给free的参数是空指针NULLfree函数则什么也不干
  • free只会释放传入的指针指向的空间,并不会给传入指针置空,这会很危险,空间你都还给操作系统了,你这个指针还指向那里,这个指针很野得治,所以free完,指针我们要置空
int mian()
{
  //开辟动态空间
  int* p = (int*)malloc(sizeof(int) * 10);
  //判断动态内存是否开辟成功
  if (p == NULL)
  {
    printf("malloc 开辟空间失败\n");
    return 1;
  }
  /*
  对动态开辟空间的使用
  */
  //不用啦,释放空间
  free(p);
  p = NULL;
  return 0;
}

👆这段代码就是动态内存的使用的基本模式,先开辟空间,然后判断是否开辟成功,接着使用这块空间,最后不用了把这块空间释放掉

✈️calloc函数

C语言中还提供了calloc函数,同样是进行动态内存分配的,函数声明如下:

void* calloc(size_t num, size_t size);

callocmalloc仅有两点不同

  • 使用上calloc函数需要两个参数,开辟空间为num个大小为size个字节的空间
  • calloc函数开辟的空间会把每个字节全部初始化为0
int main()
{
  int* p = (int*)calloc(10, sizeof(int));
  if (p == NULL)//判断空间是否开辟成功
  {
    printf("calloc开辟空间失败\n");
    return 1;
  }
  for (int i = 0; i < 10; i++)//打印动态内存中的元素
  {
    printf("%d ", p[i]);
  }
  free(p);//空间释放
  p = NULL;//指针置空
  return 0;
}

输出:

0 0 0 0 0 0 0 0 0 0  //十个0

✈️realloc函数

铁子们估计有点不买账了,说好的动态内存分配,讲了这么多也没有体现动态俩字。铁子们别急,realloc函数才是今天的重头戏

C语言提供realloc函数用来使动态内存的使用更加灵活,之前开辟的空间太大或者太小都可以灵活的调整重新分配空间大小

realloc函数的函数声明

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

realloc函数在使用时要注意下面这几点:

  • ptr是要调整的空间的起始地址,ptr得是动态内存开辟的地址,若是ptr为空指针,realloc函数将与malloc函数的实现一样
  • sizerealloc函数调整大小后的内存空间大小
  • realloc函数的返回值是空间调整后的起始地址,空间开辟失败返回空指针
  • realloc函数扩容的空间里面的值并未被初始化,是随机值
  • 这个函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
  • 对于realloc函数调整空间有了两种情况:
  • 情况一:原有空间后有足够空间

  • 情况二:原有空间后无足够空间,原有空间将被释放掉,重新找一块空间

例子:

int main()
{
  int* p = (int*)malloc(5 * sizeof(int));//先用malloc申请5个整型大小的内存空间
  if (p == NULL)//判断是否开辟成功
  {
    printf("malloc开辟空间失败\n");
    return 1;
  }
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    *(p + i) = 1;//把5个整型全部初始化为1
  }
  //不够用了,要再增加5个整型
  int* ptr = (int*)realloc(p, 10 * sizeof(int));
  if (ptr == NULL)
  {
    printf("realloc开辟空间失败\n");
    return 1;
  }
  p = ptr;//空间扩容成功将ptr的地址赋给p
  ptr = NULL;//ptr置空
  for (i = 0; i < 10; i++)
  {
    printf("%d ", *(p + i));
  }
  free(p);
  p = NULL;
  return 0;
}

上述,使用realloc对空间做调整时,一定要新创建一个变量来接收realloc的返回值,因为如果使用指向原空间的起始地址的指针p来接收,一旦realloc开辟空间失败,p将被赋值成NULL,将找不到原来空间造成内存泄漏

🚀在使用动态内存函数时的常见错误

✈️对NULL指针的解引用

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

✈️对非动态开辟内存使用free释放

void test()
{
    int a = 10;
    int *p = &a;
    free(p);//error——不能对非动态内存进行释放
}

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

void test()
{
    int *p = (int *)malloc(100);
    p++;
    free(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);
}

对于动态开辟的内存一定要记得释放,否则将造成内存泄漏


如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持博主,如有不足还请指点,博主及时改正,感谢大家支持!!!

相关文章
|
1天前
|
存储 编译器 C语言
C语言进阶⑱(文件上篇)(动态通讯录写入文件)(文件指针+IO流+八个输入输出函数)fopen+fclose(下)
C语言进阶⑱(文件上篇)(动态通讯录写入文件)(文件指针+IO流+八个输入输出函数)fopen+fclose
8 0
|
1天前
|
C语言
C语言进阶⑱(文件上篇)(动态通讯录写入文件)(文件指针+IO流+八个输入输出函数)fopen+fclose(中)
C语言进阶⑱(文件上篇)(动态通讯录写入文件)(文件指针+IO流+八个输入输出函数)fopen+fclose
8 0
|
1天前
|
存储 数据库 C语言
C语言进阶⑱(文件上篇)(动态通讯录写入文件)(文件指针+IO流+八个输入输出函数)fopen+fclose(上)
C语言进阶⑱(文件上篇)(动态通讯录写入文件)(文件指针+IO流+八个输入输出函数)fopen+fclose
8 0
|
1天前
|
程序员 编译器 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
5 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
|
6天前
|
存储
浮点数在内存中的存储
浮点数在内存中的存储
26 0
|
6天前
|
存储
数据在内存中的存储之整数存储
数据在内存中的存储之整数存储
21 0
|
4天前
|
存储 算法 关系型数据库
实时计算 Flink版产品使用合集之在Flink Stream API中,可以在任务启动时初始化一些静态的参数并将其存储在内存中吗
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
17 4
|
5天前
|
存储 小程序 编译器
数据在内存中的存储(探索内存的秘密)
数据在内存中的存储(探索内存的秘密)
11 0
|
6天前
|
存储 监控 NoSQL
Redis处理大量数据主要依赖于其内存存储结构、高效的数据结构和算法,以及一系列的优化策略
【5月更文挑战第15天】Redis处理大量数据依赖内存存储、高效数据结构和优化策略。选择合适的数据结构、利用批量操作减少网络开销、控制批量大小、使用Redis Cluster进行分布式存储、优化内存使用及监控调优是关键。通过这些方法,Redis能有效处理大量数据并保持高性能。
27 0
|
1天前
|
存储 弹性计算 监控
【阿里云弹性计算】深入阿里云ECS配置选择:CPU、内存与存储的最优搭配策略
【5月更文挑战第20天】阿里云ECS提供多种实例类型满足不同需求,如通用型、计算型、内存型等。选择CPU时,通用应用可选1-2核,计算密集型应用推荐4核以上。内存选择要考虑应用类型,内存密集型至少4GB起。存储方面,系统盘和数据盘容量依据应用和数据量决定,高性能应用可选SSD或高效云盘。结合业务特点和预算制定配置方案,并通过监控应用性能适时调整,确保资源最优利用。示例代码展示了使用阿里云CLI创建ECS实例的过程。
36 5