C语言·动态内存管理

1. 为什么要有动态内存管理?

例1:

//固定的向内存申请4个字节
int a = 10;

//申请连续的一块空间
int arr[10];

这些数据一旦声明定义之后就会在内存中有一块空间,这些空间都是固定的,为了让内存使用更加灵活,这时我们引入了动态内存分配

2. 动态内存分配的函数

使用这些函数之前,我们需要包含头文件stdlib,内存的申请都是在堆区上进行的

->1. malloc 函数

malloc向系统申请内存空间,申请到的空间没有初始化,直接返回的起始地址

开辟成功,则返回一个开辟好空间的指针

开辟失败,则返回一个NULL

若参数为0,nalloc的行为是标准未定义的,取决于编译器

void* malloc(size_t size);

(1). 需要开辟空间的总大小

例1:

#include <stdio.h>
#include <stdlib.h>

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) = 0;
    }
    for(i = 0; i < 10; i++)
    {
        printf("%d", p + i);
    } 
    free(p);      
    p = NULL;
}

可以看到p向系统申请了10个字节的空间,在我们的操作下都赋值为了0

我们来看看将p释放之后:

将p释放了之后p还是指向的原地址,即:释放之后p变成了野指针

所以当我们释放了p之后,再将它手动置0是最安全的

->2. free函数

只能释放malloc ,calloc,realloc向内存申请的空间

如果free函数的参数为NULL,那么free函数不会进行任何操作

void free( void *memblock );

(1). void *memblock 需要释放的空间地址

例1:

以malloc为例:

#include <stdio.h>
#include <stdlib.h>

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) = 0;
    }
    for(i = 0; i < 10; i++)
    {
        printf("%d", p + i);
    } 
    free(p);      
    p = NULL;
}

->3.calloc函数

在堆上申请空间(申请好空间后,会把空间初始化为0,然后返回起始地址)

void *calloc( size_t num, size_t size );

(1). size_t num 需要开辟空间的个数

(2). size_t size 每个的大小

例1:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *p = (int*)calloc(10, sizeof(int));
    if(p == NUll)
    {
       perror("calloc");
       return 1;
    }
    int i = 0;
    for(; i < 10; i++)
    {
        printf("%d", *(p + i));
    }
    free(p);
    p = NULL;
    return 0;
}

->4. realloc函数

有时我们会发现过去我们申请的内存小了,有时发现我们申请的内存大了为了应对这种情况,C语言引入了realloc函数来调整内存。

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

(1). void *memblock 要调整空间的地址

(2).size_t size 需要调整的空间的大小 

例1:

#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 < 5; i++)
    {
         *(p + i) = 1;
    }
    for(i = 0; i < 5; i++)
    {
        printf("%d ", *(p + i));
    }
    printf("\n");
    int* ptr = (int*)realloc(p, sizeof(int) * 10);
    if(ptr == NULL)
    {
       perror("realloc");
       return 1;
    }
    p = ptr;
    for(; i < 10; i++)
    {
         *(p + i) = 1;
    }
    for(i = 0; i < 10; i++)
    {
        printf("%d ", *(p + i));
    }
    free(p);
    p = NULL;
    return 0;
}

realloc的工作原理:

如例1所述:

->1. 后边有足够大的空间可以扩容时,realloc会直接在原有的基础上向后续上新的空间,返回旧的初始地址

->2. 后边没有足够大的空间可以扩容时,realloc函数会找一个满足空间大小的新的连续的空间,把旧的空间的数据拷贝到新空间的前面的位置,并且把旧的空间释放掉,同时返回新的空间的地址

->3. 如果realloc函数的参数是NULL,那么realloc函数的作用和malloc是一样的

例2:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    int* p = (int*)realloc(NULL, 40);
    if(p == NULL)
    {
       printf("%s", strerror(errno));
       return 1;
    }
    int i = 0;
    for(; i < 10; i++)
    {
         *(p + i) = 1;
    }
    for(i = 0; i < 10; i++)
    {
        printf("%d ", *(p + i));
    }
    free(p);
    p = NULL;
    return 0;
}

运行结果:

由图可知当realloc的参数是NULL时,它的作用和malloc是一样的

3. 常见的动态内存错误

->1. 对NULL解引用操作

malloc的返回值一定要判断

例1:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(40);
    if(p == NULL)
    {
       perror("malloc");
       return 1;
    }
    //判断之后在使用
    return 0;
}

->2.对动态开辟内存的越界访问

越界访问,程序终端就挂了,卡死了

例1:

#include <stdio.h>
int main()
{
    int* p = (int*)malloc(100);
    if(p == NULL)
    {
       perror("malloc");
       return 1;
    }
    int i = 0;
    for(i = 0; i < 100; i++)
    {
        *(p + i) = 0;
    }
    free(p);
    p = NULL;
    return 0;
}

运行结果:

->3. 对非动态开辟内存使用free释放

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

例1:

#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++;
    }
    free(p);
    p = NULL;
    return 0;
}

运行结果:

成因:

p指向的已经不再是这一百个字节的起始位置了或者指向这个空间的一部分,不是起始位置 

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

例1:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(100);
    if(p == NULL)
    {
       perror("malloc");
       return 1;
    }
    free(p);
    free(p);
    return 0;
}

运行结果:

程序直接挂掉

->6. 动态内存忘记释放(内存泄漏)

如果在函数中没有及时的释放动态内存,等函数销毁之后就没有机会了,只能等程序结束

例1:

#include <stdio.h>
#include <stdlib.h>

void test()
{
    int* p = (int*)malloc(100);
    if(P == NULL)
    {
       perror("malloc");
       return 1;
    }
    int i;
    for(i = 0; i < 25; i++)
    {
        *(p + i) = 1;
    }
    for(i = 0; i < 25; i++)
    {
        printf("%d ", *(p + i));
    } 
}

int main()
{
    test();
    return 0;
}

解决方法:动态内存开辟函数应该和free函数成对使用

例2:

在函数中申请的内存没有使用完,将malloc开辟的空间的起始地址返回到main函数中继续使用,在使用完之后记得释放

#include <stdio.h>
#include <stdlib.h>

int* test()
{
    int* p = (int*)malloc(100);
    if(p == NULL)
    {
       perror("malloc");
       return 1;
    }
    return p;
}
int main()
{
    int* ptr = test();
    free(ptr);
    ptr = NULL;
    return 0;
}

4.为什么需要用free释放申请的内存

虽然我们不使用free释放空间到程序结束也会呗系统释放掉,但是我们如果碰到一直运行的程序呢!

while(1)
{
    malloc(1);    
}

他会一直吃掉系统的内存,导致系统的内存越来越少

用free释放空间也需要找准时机

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(100);
    if(p == NULL)
    {
       perror("malloc");
       return 1
    }
    if(1)
    return 1;
    free(p);
    p = NULL;
    return 0;
}

这样的就是释放时机没把握好,该代码应该在if语句上面释放

5.例题

例1:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void GetMemory(char* p)
{
    p = (char*)malloc(100);
}

void test(void)
{
    char* str = NULL;
    GetMemory(str);
    strcpy(str, "Hello World");
    printf(str);
    return 0;
}

int main()
{
    test();
    return 0;
}

运行结果:

错误原因:

1.str传给p值的时候,p是str的一份临时拷贝,有自己独立的空间GetMemory在向系统申请空间之之后,放入了p中,在GetMemory返回之后,str的值依旧是NULL,即在strcpy拷贝时,形参非法范问了空间

2.在GetMemory函数申请空间之后,内存没有能及时的释放,造成了空间泄漏

将例1修改正确:

->1.第一种改法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void GetMemory(char** p)
{
    *p = (char*)malloc(100);
}

void test(void)
{
    char* str = NULL;
    GetMemory(&str);
    strcpy(str, "Hello World");
    printf(str);
    free(str);
    str = NULL;
    return 0;
}

int main()
{
    test();
    return 0;
}

运行结果:

->2.第二种改法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* GetMemory()
{
    char* p = (char*)malloc(100);
    return p;
}

void test(void)
{
    char* str = NULL;
    str = GetMemory();
    strcpy(str, "Hello World");
    printf(str);
    free(str);
    str = NULL;
    return 0;
}

int main()
{
    test();
    return 0;
}

运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spark SQL 血缘解析方案

背景 项目背景建设数据中台,往往数据开发人员首先需要能够通过有效的途径检索到所需要的数据,然后根据检索的数据模型进行业务加工然后得到一些中间模型,最后再通过数据抽取工具或者OLAP分析工具直接将数据仓库中加工好的公共模型输出到应用层。这里我不在去介绍数据仓库为…

【Qt】day2

文章目录 菜单栏工具栏状态栏铆接部件&#xff08;浮动窗口&#xff09;中心部件添加图片对话框模态对话框非模态对话框标准对话框&#xff08;信息对话框&#xff09;错误对话框信息对话框提问对话框警告对话框 其他标准对话框颜色对话框文件对话框 字体对话框 登录窗口布局按…

《重构》读书笔记【第1章 重构,第一个示例,第2章 重构原则】

文章目录 第1章 重构&#xff0c;第一个示例1.1 重构前1.2 重构后 第2章 重构原则2.1 何谓重构2.2 两顶帽子2.3 为何重构2.4 何时重构2.5 重构和开发过程 第1章 重构&#xff0c;第一个示例 我这里使用的IDE是IntelliJ IDEA 1.1 重构前 plays.js export const plays {&quo…

将 MinIO 与 Keycloak OIDC 集成

Keycloak是一种单点登录解决方案。使用Keycloak&#xff0c;用户使用Keycloak而不是MinIO进行身份验证。如果没有Keycloak&#xff0c;您将不得不为每个用户创建一个单独的身份 - 从长远来看&#xff0c;这将很麻烦。您需要一个集中身份解决方案来管理 MinIO 的身份验证和授权。…

Python深度学习技术

原文链接&#xff1a;Python深度学习技术 近年来&#xff0c;伴随着以卷积神经网络&#xff08;CNN&#xff09;为代表的深度学习的快速发展&#xff0c;人工智能迈入了第三次发展浪潮&#xff0c;AI技术在各个领域中的应用越来越广泛。Transformer模型&#xff08;BERT、GPT-…

3.2 文件包含漏洞渗透实战(OWASP实战训练)

3.2 文件包含漏洞渗透实战&#xff08;OWASP实战训练&#xff09; 原理及危害3.低安全级别渗透3.1本地文件包含漏洞3.2 本地文件包含漏洞webshell3.3远程文件包含漏洞 上一节讲了本地文件包含和远程文件包含 本地文件包含需要将文件传上去或者不传上去&#xff08;本地系统的一…

导出 S 参数扫描结果供 INTERCONNECT 使用

导出 S 参数扫描结果供 INTERCONNECT 使用 正文正文 有时候,对于 FDTD 无法直接进行仿真的大型仿真链路,我们需要使用 FDTD 针对单个小的模块进行仿真,再将得到的 S 参数结果导入到 INTERCONNECT 中使用,最终完成整个链路的仿真。通常完成 S 参数扫描后其状态如下图所示:…

【数据结构】栈的定义与实现(附完整运行代码)

目录 一、栈的定义 二、顺序栈 链栈比较 三、栈的实现&#xff08;顺序栈&#xff09; 3.1 ❥ 定义栈结构 3.2 ❥ 初始化 3.3 ❥ 销毁 3.4 ❥ 插入&#xff08;入栈&#xff09; 3.5 ❥ 删除 &#xff08;出栈&#xff09; 3.6 ❥ 获取栈顶元素 3.7 ❥ 判空 3.8 ❥…

1962springboot VUE社区服务平台系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot VUE社区服务平台系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架和VUE完成本系统&#xff0c;对理解vue java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和…

光泽正在褪去,所以我们又回到了人工智能领域。

光泽正在褪去&#xff0c;所以我们又回到了人工智能领域。 人工智能冬天将被私有化 自从“人工智能”这个流行词在20世纪50年代被创造出来以来&#xff0c;人工智能经历了几次繁荣和萧条周期。 一种新的技术方法看起来很有趣&#xff0c;并取得了一些成果。它被荒谬地炒作并获…

EdgeOne 边缘函数 + Hono.js + Fauna 搭建个人博客

一、背景 虽然 “博客” 已经是很多很多年前流行的东西了&#xff0c;但是时至今日&#xff0c;仍然有一部分人在维护自己的博客站点&#xff0c;输出不少高质量的文章。 我使用过几种博客托管平台或静态博客生成框架&#xff0c;前段时间使用Hono.jsFauna &#xff0c;基于 …

热电发电机越来越受到研发关注

热电发电机 (TEG) 利用热量&#xff08;或更准确地说&#xff0c;温差&#xff09;和众所周知的塞贝克效应来发电。它们的应用范围从收集可用热能&#xff0c;尤其是在工业和其他情况下“浪费”的热能&#xff0c;到在放射性同位素热发电机 (RTG) 中使用航天器的放射性电源作为…

静电场的基本方程

目录 场积分方程 通量&#xff08;高斯定理&#xff09; 环量 场微分方程 散度 旋度 小结 补充知识 立体角 场积分方程 通量&#xff08;高斯定理&#xff09; 环量 场微分方程 散度 旋度 小结 补充知识 立体角

慢性病防治新策略:诊所管理系统助力健康管理变革

慢性病&#xff0c;如高血压、糖尿病等&#xff0c;正逐渐成为全球健康领域的重要挑战。据尚普咨询的数据显示&#xff0c;全球每年有4100万人死于慢性非传染性疾病&#xff0c;占全球死亡总数的71%。而在中国&#xff0c;随着经济社会发展和卫生健康服务水平的提高&#xff0c…

OpenAI突然宣布停止向中国提供API服务!

标题 &#x1f31f; OpenAI突然宣布停止向中国提供API服务! &#x1f31f;摘要 &#x1f4dc;引言 &#x1f4e2;正文 &#x1f4dd;1. OpenAI API的重要性2. 停止服务的原因分析3. 对中国市场的影响4. 应对措施代码案例 &#x1f4c2;常见问题解答&#xff08;QA&#xff09;❓…

使用AES,前端加密,后端解密,spring工具类了

学习python的时候&#xff0c;看到很多会对参数进行加密&#xff0c;于是好奇心驱使下&#xff0c;让我去了解了下AES加密如何在java中实现。 首先 npm install crypto-js 然后在你的方法中&#xff0c;给你们前端源码看看&#xff0c;因为我用的ruoyi框架做的实验&#xff…

iSCSI driver not found和Failed to start Open-iSCSI的解决方法

案例1&#xff1a;iscsi的配置有问题 方法&#xff1a;一般的处理方法为重装iscsi-initiator-utils。 案例2&#xff1a;linux安装了多个内核&#xff0c;启动所选的内核与iscsi服务不匹配 方法&#xff1a;重启系统&#xff0c;选择对应的内核版本启动系统。 &#xff08;注…

Python 围棋

效果图 完整代码 源码地址&#xff1a;Python 围棋 # 使用Python内置GUI模块tkinter from tkinter import * # ttk覆盖tkinter部分对象&#xff0c;ttk对tkinter进行了优化 from tkinter.ttk import * # 深拷贝时需要用到copy模块 import copy import tkinter.me…

机器学习课程复习——奇异值分解

1. 三种奇异值分解 奇异值分解&#xff08;Singular Value Decomposition, SVD&#xff09;包含了&#xff1a; 完全奇异值分解&#xff08;Complete Singular Value Decomposition, CSVD&#xff09;紧奇异值分解&#xff08;Tight Singular Value Decomposition, TSVD&…

赶快收藏!全网最佳 WebSocket 封装:完美支持断网重连、自动心跳!

文章目录 一、WebSocket 基础WebSocket 的基本使用 二、封装 WebSocket 客户端WebSocketClient 类使用 WebSocketClient 类解释代码实现 三、总结优点未来改进 &#x1f389;欢迎来到SpringBoot框架学习专栏~ ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff…