C语言·动态内存管理

news2024/11/24 14:34:46

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

运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【回溯算法题记录】组合总和题汇总

组合总和 39. 组合总和题目描述初始思路后续分析 40. 组合总和 II题目描述思路&#xff08;参考代码随想录&#xff09; 39. 组合总和 题目&#x1f517; 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数…

开源网安参与编制的《代码大模型安全风险防范能力要求及评估方法》正式发布

​代码大模型在代码生成、代码翻译、代码补全、错误定位与修复、自动化测试等方面为研发人员带来了极大便利的同时&#xff0c;也带来了对安全风险防范能力的挑战。基于此&#xff0c;中国信通院依托中国人工智能产业发展联盟&#xff08;AIIA&#xff09;&#xff0c;联合开源…

教你使用 Go 语言访问智谱 AI 大模型!

AI 大模型太火爆了&#xff01;我在工作中经常使用它们&#xff0c;感觉已经离不开了&#xff01; 最近测试了智谱 AI 大模型&#xff0c;实测下来感觉还挺不错的。 官方提供了体验包&#xff0c;注册并实名认证之后有 300 万 token&#xff0c;非常壕&#xff01; 今天的股…

uni-push(2.0)常见问题,Android平台

将常用的网址一定要收藏在标签栏中&#xff0c;方便后期找&#xff0c;不然后期会很生气。 草料二维码&#xff0c;这个在线工具可以将打包生成的apk文件生成二维码&#xff0c;供测试人员测试。生成的apk只有五次下载机会&#xff0c;可点击链接后的一键上传&#xff0c;这样…

站在巨人的肩膀上 C语言理解和简单练习(包含指针前的简单内容)

1.格式化的输入/输出 1.1printf函数 printf函数你需要了解的就是转换说明&#xff0c;转换说明的作用是将内存中的二进制转换成你所需要的格式入%d就是将内存中存储的变量的二进制转化为十进制并打印出来&#xff0c;同时我们可以在%X的转换说明对精度和最小字段宽度的指定&a…

如何在SpringBoot中自定义starter

如何在SpringBoot中自定义starter Spring Boot 提供了一种简便的方法来创建自定义的 starter&#xff0c;从而帮助开发者封装常用的配置和依赖。本文将介绍如何在 Spring Boot 中自定义一个 starter。 1. 创建 Maven 项目 首先&#xff0c;创建一个新的 Maven 项目&#xff…

SpringBoot开启事务日志

一般框架开启日志的方式&#xff1a; 开启某个包下的日志就写该包路径&#xff0c;开启某个类下的日志就写该类路径。

全国首场以AI数字内容风控为主题的大会,开放参会报名中

网易易盾将于2024年7月6日举办一场AI数字内容风控大会&#xff0c;邀请AI产业链的基础层、模型层和应用层的企业代表&#xff0c;科研机构、律所、院校的专家老师&#xff0c;探讨大模型时代下的自由与责任等话题。参会报名链接&#xff1a;https://sourl.cn/vqUU7X&#xff0c…

【计算机毕业设计】基于Springboot的月度员工绩效考核管理系统【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…

HTTP/2 头部压缩 Header Compress(HPACK)详解

文章目录 1. HPACK 的工作原理1.1 静态表1.2 动态表 2. 压缩过程2.1 编码过程2.2 解码过程 3. HPACK 的优势 在HTTP1.0中&#xff0c;我们使用文本的形式传输header&#xff0c;在header中携带cookie的话&#xff0c;每次都需要重复传输几百到几千的字节&#xff0c;这着实是一…

推荐系统-FM模型

参考&#xff1a;推荐系统&#xff08;三&#xff09;Factorization Machines&#xff08;FM&#xff09;_svmmf-CSDN博客 一句话概括下FM&#xff1a;隐式向量特征交叉----类似embedding的思想 LR 如果利用LR做特征的二阶交叉&#xff0c;有&#xff1a; 但这个公式存在显著…

从0开始C++(七):继承

相关文章&#xff1a; 从0开始C&#xff08;一&#xff09;&#xff1a;从C到C 从0开始C&#xff08;二&#xff09;&#xff1a;类、对象、封装 从0开始C&#xff08;三&#xff09;&#xff1a;构造函数与析构函数详解 从0开始C&#xff08;四&#xff09;&#xff1a;作…

香港服务器托管对外贸行业必要性和优势

在当今全球化的经济环境下&#xff0c;外贸企业面临着前所未有的机遇与挑战。其中&#xff0c;服务器托管的选择对于外贸企业的运营效率和市场拓展具有举足轻重的作用。香港服务器&#xff0c;凭借其独特的地理位置、优质的网络环境和卓越的服务性能&#xff0c;一直是外贸企业…

2024年必备的15个免费 SVG 设计资源

在动态设计领域&#xff0c;SVG&#xff08;可缩放矢量图形&#xff09;已成为设计师打造响应迅速、清晰且适应性强的视觉效果的必备工具。 这些设计非常适合幻灯片 PowerPoint 演示文稿、应用程序设计、网站设计、原型设计、社交媒体帖子等。 在这篇文章中&#xff0c;我们将…

这份AI绘画攻略赶紧码住!超适合小白入门的PS AI插件来啦!

有没有小伙伴对AI绘画很感兴趣&#xff0c;但是看到国外的mj和sd总觉得入门困难&#xff01;别担心&#xff0c;米兔挖到一款超级绝的国产PS AI插件&#xff01;适合新手学习&#xff0c;米兔这里还有一份专为小白准备的AI绘画攻略&#xff0c;让你的创意不再受限&#xff01; …

评测|贪吃小猫疯狂长肉,让它停不下嘴的希喂、鲜朗、帕特真实调研

我发现很多铲屎官存在一个误区&#xff0c;认为“进口即是高贵”&#xff0c;过度信赖进口产品。一见到进口宠物粮就冲动购买&#xff0c;甚至对国产品牌持贬低态度&#xff0c;贴上“质量不佳”、“不符合标准”等标签。 为了更深入地了解这一现象&#xff0c;我深入研究了主食…

minio+tusd+uppy搭建文件上传服务

1、docker部署minio、tusd服务 1.1 新建docker-compose.yml minio API: http://ip:9100 minio控制台: http://ip:9101 tus API: http://ip:9102/files/ tus webhooh: http:172.0.0.1:3000/files/webhooh(用户鉴权API) version: 3.7services:minio:image: minio/minio:RELEAS…

工业软件的分类与选择策略:针对中小企业的实际应用考量

工业软件是现代工业体系的“大脑”&#xff0c;已经渗透到几乎所有工业领域的核心环节&#xff0c;是现代产业之“魂”&#xff0c;是制造强国之重器。工业软件通过优化生产流程、实时监控设备状态、实现自动化控制等功能&#xff0c;可以帮助企业显著提升生产效率和质量&#…

python爬虫-爬虫的基础知识储备

爬虫就是一个不断的去抓去网页的程序&#xff0c;根据我们的需要得到我们想要的结果&#xff01;但我们又要让服务器感觉是我们人在通过浏览器浏览不是程序所为&#xff01;归根到底就是我们通过程序访问网站得到html代码&#xff0c;然后分析html代码获取有效内容的过程。下面…

透明屏幕的魅力:为何它如此受欢迎

在科技日新月异的今天&#xff0c;透明屏幕技术以其独特的魅力和广泛的应用前景&#xff0c;逐渐成为了科技领域的一颗璀璨明星。从智能手机、平板电脑到大型显示屏&#xff0c;透明屏幕技术以其前所未有的视觉体验和实用性&#xff0c;赢得了广大消费者的喜爱。 一、透明屏幕的…