【C语言】动态内存分配

1、为什么要有动态内存分配

不管是C还是C++中都会大量的使用,使用C/C++实现数据结构的时候,也会使用动态内存管理。

我们已经掌握的内存开辟方式有:

    int val = 20;            //在栈空间上开辟四个字节
    char arr[10] = { 0 };    //在栈空间上开辟10个字节的连续空间

 上面的内存申请方式,一旦申请好空间,大小就无法调整。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才知道,那数组在编译时开辟空间的方式就不能满足了。

C语言引入了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活。

2、malloc和free

2.1 malloc

C语言提供了一个动态内存开辟的函数:

void* malloc ( size_t size );

这个函数向内存申请了一块连续可用的空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个 NULL 指针,因此malloc的返回值一定要作检查。
  • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
#include <stdio.h>
#include <stdlib.h>
int main()
{//申请20个字节的空间 - 存放5个整数int* p = (int*)malloc(20);//强制转换为整型//判断是否开辟成功if (p == NULL){perror("malloc");return 1;}//开辟成功,使用空间int i = 0;for (i = 0; i < 5; i++){*(p + i) = i + 1;}return 0;
}

 2.2 free

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

void free ( void* ptr );   //传递给free函数的是要释放的内存空间的起始地址

 free 函数用来释放动态开辟的内存。

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

malloc 和 free 都声明在 stdlib.h 头文件中。

free 函数的意思是把空间的使用权限还给操作系统,一旦free完ptr就变成野指针,所以给ptr置空(ptr = NULL)。

举个例子:

#include <stdio.h>
#include <stdlib.h>
int main()
{//申请20个字节的空间 - 存放5个整数int* p = (int*)malloc(20);//强制转换为整型//判断是否开辟成功if (p == NULL){perror("malloc");return 1;}//开辟成功,使用空间int i = 0;for (i = 0; i < 5; i++){*(p + i) = i + 1;}//释放内存free(p);//传参是要释放内存空间的起始地址p = NULL;return 0;
}

3、calloc 和 realloc

3.1 calloc

C语言提供了一个函数叫 calloc 、calloc函数也是用来动态内存分配的。原型如下:

void* calloc ( size_t num,size_t size );

  • 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
  • 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0
  • 函数malloc没有初始化内存,它效率更快。

举个例子:

//使用calloc函数向内存申请5个整型的空间
int main()
{int* p = (int*)calloc(5 ,sizeof(int));if (p == NULL){perror("calloc");return 1;}int i = 0;for (i = 0; i < 5; i++){printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}

 输出结果:

0 0 0 0 0

为什么输出全0?

因为calloc函数把申请的空间的每个字节初始化为全0。 

如果我们要对申请的内存空间的内容要求初始化,可以使用calloc函数来完成任务。

3.2 realloc

  • realloc函数的出现让动态内存管理更加灵活。
  • 有时我们会发现过去申请的空间太小了,有时候我们又会觉得申请的空间太大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的调整,那reallo函数就可以做到对动态开辟内存大小的调整。

函数原型如下:

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

  • ptr是要调整的内存地址。
  • size调整之后新大小。
  • 返回值为调整之后的内存起始位置。
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中是数据移动到新的空间。
  • realloc在调整内存空间的是存在两种情况:
    • 情况1:原有空间之后又足够大的空间
    • 情况2:原有空间之后没有足够大的空间
    • 情况3:调整失败,返回空指针

 情况2解决方法:在堆区的内存找一个新的空间,并且新的大小要求会在原来空间的数据拷贝一份到新的空间。释放旧的空间。返回新的内存空间的起始地址。

#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(sizeof(int) * 5);if (p == NULL){perror("malloc");return 1;}//使用空间int i = 0;for (i = 0; i < 5; i++){*(p + i) = i + 1;}//希望将空间调整为40个字节  //扩展容量//注意:①先将realloc的返回值放到ptr中int* ptr = realloc(p, 40);if (ptr != NULL)  //调整成功{//注意:②ptr不为NULL,就把ptr赋值给pp = ptr;int i = 0;for (i = 5; i < 10; i++){*(p + i) = i + 1;}for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放空间free(p);p = NULL;}else  //调整失败{perror("realloc");//失败就在这里使用20空间free(p);return 1;}return 0;
}

4、常见的动态内存的错误

4.1 对NULL指针的解引用操作

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

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

//对动态开辟空间的越界访问
int main()
{int* p = (int*)malloc(10 * sizeof(int));if (NULL == p){exit(EXIT_FAILURE);}for (int i = 0; i <= 10; i++){*(p + i) = i + 1;//当i是10 越界访问}free(p);
}

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

int main()
{int a = 10;int* p = &a;free(p);
}

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

int main()
{int* p = (int*)malloc(5 * sizeof(int));p++;free(p);//p不是动态内存的起始位置
}

4.5 对同一块内存多次释放

int main()
{int* p = (int*)malloc(5 * sizeof(int));free(p);free(p);//重复释放
}

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

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

切记:谁创建谁释放   //malloc 和 free 成对出现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/562436.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用Base64加密算法将数据加密解密

1. Base64加密算法 Base64准确来说并不像是一种加密算法&#xff0c;而更像是一种编码标准。 我们知道现在最为流行的编码标准就是ASCLL&#xff0c;它用八个二进制位&#xff08;一个char的大小&#xff09;表示了127个字符&#xff0c;任何二进制序列都可以用这127个字符表…

【小沐学AI】智谱AI大模型的一点点学习(Python)

文章目录 1、简介1.1 大模型排行榜 2、智谱AI2.1 GLM2.1.1 模型简介2.1.2 开源代码2.1.2.1 GLM-130B 2.2 ChatGLM2.2.1 模型简介2.2.2 开源代码2.2.2.1 ChatGLM-6B2.2.2.2 ChatGLM3 2.3 CodeGeeX2.3.1 模型简介2.3.2 开源代码 2.4 CogView2.4.1 模型简介2.4.2 开源代码 2.5 Cog…

关于序列化和反序列化

什么是序列化&#xff0c;什么是反序列化 简单来说&#xff1a; 序列化&#xff1a;将数据结构或对象转换成二进制字节流的过程反序列化&#xff1a;将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程 为什么要进行序列化 我们要将java对象进行网络传输&a…

Grok-1安装

安装 源代码 huggingface git clone https://github.com/xai-org/grok-1.git # 新建虚拟环境 conda create --prefixD:\CondaEnvs\grok1 python3.11conda activate D:\CondaEnvs\grok1 pip install huggingface_hub[hf_transfer] pip install -U "huggingface_hub[cli]&…

#Linux(Source Insight安装及工程建立)

&#xff08;一&#xff09;发行版&#xff1a;Ubuntu16.04.7 &#xff08;二&#xff09;记录&#xff1a; &#xff08;1&#xff09;安装教程&#xff1a; Source insight 工具安装及使用方法-CSDN博客https://blog.csdn.net/YAOHAIPI/article/details/125191451&#xff…

浅谈前端路由原理hash和history

1、认识前端路由 本质 前端路由的本质&#xff0c;是监听 url 地址或 hash 值的改变&#xff0c;来切换渲染对应的页面组件 前端路由分为两种模式 hash 模式 history 模式 两种模式的对比 2、hash 模式 &#xff08;1&#xff09;hash 定义 hash 模式是一种把前端路由的路…

SpringCloud之网关组件Gateway学习

SpringCloud之网关组件Gateway学习 GateWay简介 Spring Cloud Gateway是Spring Cloud的⼀个全新项目&#xff0c;目标是取代Netflix Zuul&#xff0c;它基于Spring5.0SpringBoot2.0WebFlux&#xff08;基于高性能的Reactor模式响应式通信框架Netty&#xff0c;异步⾮阻塞模型…

Wireshark TS | DNS 案例分析之外的思考

前言 承接之前一篇《Packet Challenge 之 DNS 案例分析》&#xff0c;在数据包跟踪文件 dnsing.pcapng 中&#xff0c;关于第 4 题&#xff08;What is the largest DNS response time seen in this trace file? &#xff09;的分析过程中曾经碰到一个小问题&#xff0c;主要…

Pycharm小妙招之Anaconda离线配环境

Pycharm小妙招之Anaconda离线配环境———如何给无法联网的电脑配python环境&#xff1f; 1. 预备工作2. 电脑1导出包2.1 环境路径2.2 压缩py38导出至U盘 3. 电脑2导入包4. 验证是否导入成功4.1 conda查看是否导入4.2 pycharm查看能否使用 1. 预备工作 WINDOWS系统电脑1(在线)…

设计模式及其在项目、框架中的应用

设计模式的作用&#xff1a; 1、类之间关系图&#xff0c;明确的角色及其关系、作用&#xff1b; 2、符合开闭原则&#xff0c;职责明确&#xff0c;并且开放的拓展点可以有效应对后期的变化。 &#xff08;一&#xff09;、责任链模式 适用场景&#xff1a; 在一个流程中&…

mysql无法看到3306端口监听

参考:https://blog.csdn.net/shumeigang/article/details/103902459 mysql> show global variables like ‘port’; 是0 原因是我的my.cnf有话&#xff1a; skip-network 或 注释掉&#xff0c;然后重新启动下数据库&#xff0c;运行netstat -an|grep 3306 就可以看到了

如何进行设备的非对称性能测试

非对称性能测试介绍 RFC2544是RFC组织提出的用于评测网络互联设备&#xff08;防火墙、IDS、Switch等&#xff09;的国际标准。主要是对RFC1242中定义的性能评测参数的具体测试方法、结果的提交形式作了较详细的规定。标准中定义了4个重要的参数&#xff1a;吞吐量&#xff08…