首页 > 系统相关 >2024-02-18-物联网C语言(6-动态内存申请)

2024-02-18-物联网C语言(6-动态内存申请)

时间:2024-02-18 17:12:23浏览次数:30  
标签:02 char malloc 18 申请 2024 内存 空间 100

6.动态内存申请

6.1 动态分配概述

​ 在数组一章中,介绍过数组的长度是预先定义好的,在整个程序中固定不变,但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定

​ 为了解决上述问题,C语言提供了-些内存管理函数,这些内存管理函数可以按需要动态的分配内存空间,也可把不再使用的空间回收再次利用。

	**动态分配内存就是在堆区分配空间**

6.2 静态分配、动态分配

静态分配
1、在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式。int a[10]
2、必须事先知道所需空间的大小
3、分配在栈区或全局变量区,一般以数组的形式
4、按计划分配

动态分配
1、在程序运行过程中,根据需要大小自由分配所需空间
2、按需分配
3、分配在堆区,一般使用特定的函数进行分配

6.3 动态分配函数

  1. malloc函数

    #include <stdlib.h>
    void *malloc(unsigned int size);
    // 功能:在堆区开辟指定长度的空间
    // 参数:
    // 	size:要开辟的空间的大小返回值:
    // 	成功:开辟好的空间的首地址
    //	失败:NULL
    

    注意
    1. 在调用 malloc之后,一定要判断一下,是否申请内存成功
    2. 如果多次 malloc,申请的内存,第1次和第2次申请的内存不一定是连续的
    3. 使用malloc开辟空间需要保存开辟好的空间首地址,但是不确定空间的用途,所以本身的返回值是void *,使用的时候需要根据实际情况进行强制类型转换

    #include <stdio.h>
    #include <stdlib.h>
    
    char *func(){
        //char ch[100]= "hello world";
        //静态全局区的空间只要开辟好,除非程序结束,否则不会释放,所以//如果是临时使用,不建议使用静态全局区的空间
        //static char ch[100]= "hello world";
        //堆区开辟空间,手动申请手动释放,更加灵活
        char *str =malloc(100 *sizeof(char));
        str[0] = 'h';
        str[1] = '\0';
        return str;
    }
    
    
    int main(int argc, char const *argv[])
    {
        char *p;
        p = func();
        printf("p = %s\n",p);
        return 0;
    }
    

    输出结果

    p = h
    
  2. free函数(释放内存函数)

    头文件:#include <stdlib.h>
    函数定义:void free(void *ptr)
    功能:只能释放堆区的空间
    参数:ptr ,开辟后使用完毕的堆区空间首地址
    返回值:无
    
    #include <stdio.h>
    #include <stdlib.h>
    
    char *func(){
        //char ch[100]= "hello world";
        //静态全局区的空间只要开辟好,除非程序结束,否则不会释放,所以//如果是临时使用,不建议使用静态全局区的空间
        //static char ch[100]= "hello world";
        //堆区开辟空间,手动申请手动释放,更加灵活
        char *str =malloc(100 *sizeof(char));
        str[0] = 'h';
        str[1] = '\0';
        return str;
    }
    
    
    int main()
    {
        char *p;
        p = func();
        printf("p = %s\n",p);
        // 使用free释放空间
        free(p);
        // 防止野指针
        p = NULL;
        return 0;
    }
    

    注意:

    1. free函数只能释放堆区的空间,其他区域的空间无法使用free
    2. free释放空间必须释放malloc或者calloc或者reloc的返回值对应的空间,不能说只释放一部分free(p)
    3. 当free后,因为没有给p赋值,所以p还是指向原先动态申请的内存。但是内存已经不能再用了,p变成野指针了,所以一般为了放置野指针,会free完毕之后对p赋为NULL
    4. 一块动态申请的内存只能free一次,不能多次free
  3. calloc函数

    头文件:#include <stdlib.h>
    函数定义:void *calloc(size_t nmemb,size_t size)
    功能:在堆区申请制定大小的空间
    参数:
        nmemb:要申请的空间块数
        size:每块的字节数
    返回值:
        成功:申请空间的首地址
        失败:NULL
    

    注意:
    malloc 和 calloc函数都是用来申请内存的。
    区别:

    1. 函数的名字不一样
    2. 参数的个数不一样
    3. malloc 申请的内存,内存中存放的内容是随机的,不确定的,而 *calloc函数申请的内存中的内容为0
    // 在堆中申请了3块,每块大小为 100个字节,即 300个字节连续的区域
    char *p=(char *)calloc(3,100);
    
  4. realloc函数(重新申请内存)

    在调用 malloccalloc函数单次申请的内存是连续的,两次申请的两块内存不一定连续。

    有些时候有这种需求:先用 malloc 或者 calloc,申请了一块内存,还想在原先内存的基础上挨着申请内存。或者开始时候使用 malloc 或 callo 申请了一块内存,再释放后边的一部分内存。

​ 为了解决这个问题,就发明了realloc这个函数。

头文件 #include<stdlib.h>
函数的定义: void* realloc(void *s,unsigned int newsize);
参数:
    s - 原本开辟好的空间首地址
    newsize - 重新开辟空间的大小
返回值:
    新的空间的首地址
    
函数的功能: 在原先s指向的内存基础上重新申请内存,新的内存的大小为 new size 个字节
    1. 如果原先内存后面有足够大的空间,就追加;
    2. 如果后边的内存不够用,则relloc函数会在堆区找一个 newsize个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。
char *p;
p = (char *)malloc(100);

// 在100个字节后面追加50个字节
p = (char *)realloc(p,150); 

// 将100个字节减少到50个字节
p = (char *)realloc(p,50); 

注意:malloc calloc relloc 动态申请的内存,只有在fee或程序结束的时候才释放。

6.4 内存泄露

​ 内存泄露的概念: 申请的内存,首地址丢了,找不了,再也没法使用了,也没法释放了,这块内存就被泄露了。

案例1

int main(){
    char *p;
	p=(char *)malloc(100);
    
	//接下来,可以用p指向的内存了
	p="hello world";//p指向别的地方了
    
    //从此以后,再也找不到你申请的100个字节了,则动态申请的100个字节就被泄露了
	return 0;
}

案例2

void fun(){
    char *p;
    p=(char *)malloc(100);//接下来,可以用p指向的内存了
}

// 不断地申请空间,导致内存炸裂
int main() {
    fun();
    // 后面每次调用一次fun泄露100个字节
    fun();
    fun();
    ...
    return 0; 
}

解决方式:

  1. 申请的空间使用完毕后,使用free释放空间,指针指向NULL
  2. 可以将内存空间地址返回,然后释放,指针指向NULL

标签:02,char,malloc,18,申请,2024,内存,空间,100
From: https://www.cnblogs.com/hasaki-yasuo/p/18019592

相关文章

  • 2024牛客寒假算法基础集训营2
    C.TokitsukazeandMin-MaxXOR题解:01Trie观察后发现对序列\(a\)排序并不影响结果然后容易知道,对于\(i<j,a_i\oplusa_j\leqk\),一共有\(2^{i-j-1}\)种序列\(b\)满足条件,特别的,如果\(i=j\),只有\(1\)种满足条件那么现在问题就转换为,我们固定\(a_......
  • 亚马逊云ec2-user安装node-js-18.16.0
    1,下载vnm管理工具curl-o-https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh|bashexportNVM_DIR="$HOME/.nvm"[-s"$NVM_DIR/nvm.sh"]&&\."$NVM_DIR/nvm.sh"#Thisloadsnvm[-s"$NVM_DIR/bash......
  • Unity 2022.3.20f1新功能,异步实例化预制体Object.InstantiateAsync
    今天查看Unity2022.3.20f1更新日志,发现新增了个异步实例化的功能,这个功能解决了Unity历史上实例化预制体卡顿的痛点,简直不要太爽。具体的API文档请点击跳转。做了个简单的实例化测试,实例化500*500个Cube,耗时9.2s。实例化过程之间不会卡顿,可以做其他事情,即便是在重度游戏加载场......
  • P9847 [ICPC2021 Nanjing R] Crystalfly
    前景导入当\(t\in[1,2]\)时,本题如何求解?答:树形dp设\(f[i]\)为以\(i\)为根的树,根节点的晶蝶已消散且儿子节点的晶蝶还未被惊动,能获得的最大晶蝶数。则有状态转移方程\(f[i]=(\sumf[u])+max(a[u])\),其中\(u\)为\(i\)的儿子。最终的答案即为\(f[1]+a[1]\)划向更......
  • PAT甲 1025 PAT Ranking
    题目:1080GraduateAdmission-PAT(AdvancedLevel)Practice(pintia.cn) 测试点4出现段错误,其他过了,找不出来哪里有问题。准备把别人代码复现一遍。 其他:1、排序函数要用&引用传参,不然会超时```在排序函数中使用引用传递可以避免不必要的对象拷贝,从而提高排序的......
  • 2024-02-17-物联网C语言(5-指针)
    5.指针5.1关于内存那点事存储器:外存和内存 外存:长期存放数据,掉电不会丢失数据,如硬盘、光盘、ROM等 内存:暂时存放数据,掉电数据丢失,如RAM,DDR等内存:物理内存和虚拟内存物理内存:实实在在的存储设备虚拟内存:操作系统虚拟出来的内存操作系统会在物理内存和虚拟内存之间做映......
  • [刷题笔记] P9751 [CSP-J 2023] 旅游巴士
    Problem_LinkDescription给定一个\(n\)个点,\(m\)条边的有向图。起点为\(1\),终点为\(n\)。起始时间和终止时间必须是\(k\)的倍数。通过每条边的时间为\(1\)。每条边有限制\(a_i\)即若通过当前边必须满足当前时间\(t\geqa_i\)。求满足上述限制的前提下,到达终点的最小......
  • ts基础类型02
    类型声明letnum:number=11num=10functiona(aaa:string){  console.log(aaa)}a('111')//类型声明,指定ts变量(参数,形参)的类型ts编译器,自动检测//类型声明给变量设置了类型后,该变量只能储存对应类型的值,如下letflag:boolean=trueflag=false......
  • 海亮02/18杂题
    海亮02/18杂题个人题单T1link题意给你一个长度为\(n\)的数列,然后给你\(q\)个交换或不交换操作,你可以选择操作或者不操作,问所有情况下逆序对的总和。答案需要对\(10^9+7\)取模。\(n\leq3000\),\(q\leq3000\)。题解发现一个问题,对于操作执不执行很难描述,怎么办?......
  • ABAP:MM01/MM02/MM03物料主数据增强
    1.屏幕增强-在主表中附加结构(判断数据的主表,如MARA,MARC)增强字段数据元素勾选更改文档以后,会记录字段变更历史 -SPRO-->物流-常规-->物料主数据-->配置物料主记录-->创建定制子屏幕的程序 会生成对应的函数组--里面会包含两个屏幕(0001,0002)这里的0001屏幕作为......