C语言-动态内存分配(12.1)

news/2025/2/14 4:58:24/

目录

思维导图:

1.为什么存在动态内存分配

2.动态内存函数的介绍

2.1 malloc

2.2 free

2.3 calloc

2.4 realloc

3.常见的动态内存错误

写在最后:


思维导图:

1.为什么存在动态内存分配

我们现在学习了一些内存开辟的方式:

int main()
{int i;//在内存栈区开辟4个字节空间char arr[5];//在栈空间上开辟5个字节的连续空间return 0;
}

但是,这样开辟的内存是静态的,固定的:

1. 空间开辟大小是固定的。

2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

如果想要在编译过程中开辟空间,就需要用到动态内存。

2.动态内存函数的介绍

2.1 malloc

void* malloc (size_t size)

2.2 free

void free (void* ptr)

例:


#include <stdio.h>
#include <stdlib.h>int main()
{//申请40给字节,用来存放10个整形int* p = (int*)malloc(40);//malloc申请的空间不会初始化if (p == NULL)            //直接返回起始地址{perror("malloc");//如果空间开辟失败要报错并返回return 1;}//使用空间int i = 0;for (i = 0; i < 10; i++){printf("%d\n", *(p + i));}//释放申请的内存free(p);p = NULL;return 0;
}

输出:

输出:
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451

malloc不会自己初始化,所以打印随机值。

例:

#include <stdio.h>
#include <stdlib.h>int main()
{//申请40给字节,用来存放10个整形int* p = (int*)malloc(40);//malloc申请的空间没有初始化if (p == NULL)            //直接返回起始地址{perror("malloc");//如果空间开辟失败要报错并返回return 1;}//使用空间int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;//初始化赋值printf("%d ", *(p + i));}//释放申请的内存free(p);p = NULL;return 0;
}

输出:

输出:1 2 3 4 5 6 7 8 9 10

2.3 calloc

void* calloc (size_t num, size_t size)

例:

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)calloc(10, sizeof(int));//10是要初始化的个数,sizeof(int)是每个的大小if (NULL == p){perror("calloc");//判断内存是否申请成功return 1;}//使用int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));//calloc申请空间后,会把空间初始化成0}                                              //再返回起始地址//释放free(p);p = NULL;
}

输出:

输出:0 0 0 0 0 0 0 0 0 0

2.4 realloc

void* realloc (void* ptr, size_t size)

realloc函数可以追加更多动态内存。

例:

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(5 * sizeof(int));//开辟一段动态内存20个字节if (NULL == p){perror("malloc");//检查是否创建成功return 1;}//使用int i = 0;for (i = 0; i < 5; i++){*(p + i) = i + 1;}//不够用,增加5个整形的空间int* ptr = (int*)realloc(p, 10 * sizeof(int));//这里是开辟到40个字节//realloc函数开辟内存空间有两种情况://1.原内存块后面空间足够,在原内存块后面追加内存//2.原内存块后面空间不够,另外找一片区域开辟内存,将原内存块释放if (ptr != NULL){p = ptr;//为什么不直接用p接收?//如果内存追加失败,直接用p接收的话,原本已经开辟的动态内存空间就会被覆盖}for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放free(p);p = NULL;return 0;
}

输出:

输出:1 2 3 4 5 -842150451 -842150451 -842150451 -842150451 -842150451

3.常见的动态内存错误

例1:

开辟动态内存记得要判断,最后释放内存。

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(100);//内存开辟后没有判断是否开辟成功int i = 0;for (i = 0; i < 10; i++){*(p + i) = 0;//危险代码,我们无法知道是否存在非法访问}//并且最后也没有将开辟的内存还给操作系统return 0;
}

例2:

动态内存开辟了多少就用多少,小心越界访问。

#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(100);//开辟了100字节空间//判断if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 100; i++)//造成越界访问{*(p + i) = 0;//一个整形4个字节}//释放free(p);p = NULL;return 0;
}

例3:

不要乱释放其他内存空间。

int main()
{int a = 10;int* p = &a;//你没有权限乱释放其他的内存空间free(p);//不能对栈区的内存释放return 0;
}

例4:

不要多次释放内存空间。

#include <stdio.h>
#include <stdlib.h>int main()
{//开辟空间int* p = (int*)malloc(100);//判断if (p == NULL){perror("malloc");return 1;}//使用int i = 0;for (i = 0; i < 25; i++){*p = i;p++;//p指针不断往后移动}//释放的时候指针应该指向起始地址,否则程序又会出错free(p);p = NULL;return 0;
}

例5:


#include <stdio.h>
#include <stdlib.h>int main()
{//创建int* p = (int*)malloc(100);//判断if (p == NULL){return 1;}//释放free(p);free(p);//已经释放了,重复释放会导致程序出错return 0;
}

这就要说到最后置为空指针的好处了:

#include <stdio.h>
#include <stdlib.h>int main()
{//创建int* p = (int*)malloc(100);//判断if (p == NULL){return 1;}//释放free(p);p = NULL;//置为空指针后程序就不会崩溃了free(p);//p为空指针时,程序不会报错return 0;
}

例5:

在实现函数时开辟了动态内存要记得及时释放或者返回地址,

不然就再也找不到那段内存空间了,最后导致内存泄漏。

#include <stdio.h>
#include <stdlib.h>void test()
{int* p = (int*)malloc(100);//忘记释放
}//出了函数就找不到了,因为变量p被销毁了
//造成内存泄漏int main()
{test();return 0;
}

例6:

这道题也是类似的:

#include <stdio.h>
#include <stdlib.h>void test()
{int* p = (int*)malloc(100);if (p == NULL){return;}//使用if (1)return;//出问题//内存泄漏//释放free(p);p = NULL;
}int main()
{test();return 0;
}

要小心出现内存泄漏,记得释放空间。

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果喜欢本文的话,欢迎点赞和评论,写下你的见解。

如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。

之后我还会输出更多高质量内容,欢迎收看。


http://www.ppmy.cn/news/12059.html

相关文章

【软件测试】软件测试分类

1. 按照测试对象划分 界面测试 界面测试&#xff08;简称UI测试)&#xff0c;测试用户界面的功能模块的布局是否合理、整体风格是否一致、各个控件的放置位置是 否符合客户使用习惯&#xff0c;此外还要测试界面操作便捷性、导航简单易懂性&#xff0c;页面元素的可用性&…

【ROS】package.xml文件解析

文章目录 文章目录 前言 一、基本格式 1.基本结构 2.必要标签 3.依赖关系 二、Metapackage包 总结 前言 ros每个功能包中都有一个包清单&#xff0c;它是一个名为package.xml的XML 文件&#xff0c;它必须包含在任何符合 catkin 的包的根文件夹中。此文件定义有关包的属…

微软的回声消除(AEC)代码

其实microsoft也有自己的AEC代码,只是很少有人关注,下面是代码示例: #include <windows.h> #include <dmo.h> #include <Mmsystem.h> #include <objbase.h> #include <mediaobj.h> #include <uuids.h> #include <propidl.h> #inc…

万字讲解Linux常用指令

目录 前言&#xff1a; 一、Linux界面问题 二、什么是操作系统 三、为什么学习Linux基本指令 四、Linux基础指令 pwd命令 ls指令 认识一下ls -a&#xff1a; 认识一下ls -d&#xff1a; 理解文件 cd指令 4.touch指令 5.mkdir指令 6.rmdir指令和rm指令 7.man指令 8.cp指令 9.mv指…

一本修炼秘籍,带你打穿文件上传的21层妖塔(1)

目录 前言 引子 第一层&#xff1a;JS限制——你在玩一种很新的防御 第二层&#xff1a;Content-Type限制——我好像在哪见过你 第三层&#xff1a;黑名单绕过——让我康康&#xff01; 前言 &#x1f340;作者简介&#xff1a;被吉师散养、喜欢前端、学过后端、练过CTF、…

MySQL基本运维命令

文章目录1. 导出test_db数据库2. 导出一个表3. 导出一个数据库结构4. 导入数据库5. mysql进入与退出6. 数据库操作7. 数据表操作8. 修改密码9. 增加用户10. 删除用户11. 数据库授权12. 锁表13. 查看当前用户14. MYSQL密码破解方法15. 存储引擎、系统版本、端口1. 导出test_db数…

C++---线性dp---最低通行费(每日一道算法2023.1.14)

题目&#xff1a; 一个商人穿过一个 NN 的正方形的网格&#xff0c;去参加一个非常重要的商务活动。 他要从网格的左上角进&#xff0c;右下角出。 每穿越中间 1 个小方格&#xff0c;都要花费 1 个单位时间。 商人必须在 (2N−1) 个单位时间穿越出去。 而在经过中间的每个小方…

Ambari2.7.5安装Flink1.14

文章目录下载Flink配置安装源下载ambari-flink-service服务修改配置文件创建用户和组重启Ambari登录Ambari安装Flink提交Flink任务Flink 直接单独提交到 On Yarn指定Flink在Yarn跑的容器运行Flink异常异常1异常2异常3下载Flink配置安装源 wget https://archive.apache.org/dis…