当前位置: 首页 > news >正文

动态内存的开辟

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

🐰动态内存管理 

🐰malloc 

🐰calloc

🐰realloc 

🐰free 

🐰动态开辟常见的错误


🐰动态内存管理 

其实我们创建数组的时候,系统为我们就是开辟了一段连续的空间(这个空间一般是在栈区开辟的),现在我们可以自己开辟一段空间。与动态开辟相关的函数:malloc free calloc realloc

注意:数组离开作用域时,系统会自动释放这段空间,如果我们自己动态开辟的空间,离开作用域时,系统是不会帮我们释放这段空间的,如果要求释放这段动态开辟的空间,我们就需要使用free函数去释放。

🐰malloc 

malloc用于动态开辟内存,引用的头文件是#include<stdlib.h>

malloc的原型:

void* malloc (size_t size);

size_t size:开辟的字节数

注意:返回的类型是void*类型,如果想要使用这段空间,就必须强制性转化为自己想使用的类型,例如:int* p=(int*)malloc(20);这就是开辟了20个字节,然后把20个字节空间的首地址赋值给了p。

malloc的使用:

malloc开辟成功会返回开辟好的空间的首地址, malloc如果申请内存失败会返回空指针NULL,所以我们开辟好空间的话,需要去判断一下

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
    int *p=(int *)malloc(20);//开辟了20个字节的空间
    if(p==NULL)
    {
        printf("%s\n",strerror(errno));//打印开辟失败的原因
    }
    free(p);
    p=NULL;
    return 0;
}

 malloc开辟空间,不初始化,里面存放的随机值

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int *p=(int *)malloc(20);//开辟了20个字节的空间
    for(int i=0;i<5;i++)
    {
        printf("%d ",*(p+i));
    }
    //使用
    for(int i=0;i<5;i++)
    {
        *(p+i)=i+1;
    }
    for(int i=0;i<5;i++)
    {
        printf("%d ",*(p+i));
    }
    free(p);
    p=NULL;
    return 0;
}

🐰calloc

calloc用于动态开辟内存,区别于malloc的是,calloc会初始化开辟的空间,将开辟的空间全部初始化为0,引用的头文件是#include<stdlib.h>

calloc的原型:

void* calloc (size_t num, size_t size);

size_t num:开辟的个数

size_t size:开辟的类型的大小

注意:返回的类型是void*类型,如果想要使用这段空间,就必须强制性转化为自己想使用的类型,例如:int* p=(int*)calloc(20,sizeof(int));这就是开辟了20个整形的空间,然后把20个整形空间的首地址赋值给了p。

calloc的使用:

 calloc开辟空间,初始化,里面存放的0

#include<stdio.h>
#include<stdlib.h>
int main()
{

    int *p=(int *)calloc(20,sizeof(int));//开辟了20个int类型的空间
    for(int i=0;i<20;i++)
    {
        printf("%d ",*(p+i));
    }
    free(p);
    p=NULL;
    return 0;
}

🐰realloc 

realloc用于原空间不足继续开辟更大的空间,引用头文件为#include<stdlib.h>

realloc的原型:

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

void* ptr:原空间的首地址

size_t size:开辟新空间的大小

注意:realloc开辟空间会遇到两种情况

1.如果原空间后面空间充足

在原空间后面继续开辟空间(返回的地址与原来的地址相同)

2.如果原空间后面空间不足

(1)realloc会找更大的空间

(2)将原来的数据拷贝到新空间

(3)释放原来的旧的空间

(4)返回新空间的地址(返回的地址与原来的地址不同)

realloc的使用:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
    int *p=(int *)malloc(20);//开辟了20个int类型的空间
    if(p==NULL)
    {
        printf("%s\n",strerror(errno));
    }
    for(int i=0;i<5;i++)
    {
        printf("%d ",*(p+i));
    }
    int* pc=(int*)realloc(p,sizeof(p)*2);//在原空间进行扩展
    if(pc!=NULL)
    {
        p=pc;//把空间首地址还是赋给p
        for(int i=0;i<10;i++)
        {
            printf("%d ",*(p+i));
        }
    }
    else
    {
        printf("realloc:%s\n",strerror(errno));//如果开辟失败,打印原因
    }
    free(p);
    p=NULL;
    return 0;
}

🐰free 

free用于释放动态开辟的空间,引用头文件#include<stdlib.h>

free的原型:

void free (void* ptr);

void* ptr:动态开辟空间的首地址

注意:释放的空间一定是动态开辟的(就是在堆上开辟的),不然free是不起作用的。

 

 free的使用:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int* p=(int *)malloc(20);//开辟了20个字节的空间
    free(p);//将20个字节的空间还给了系统
    p=NULL;//如果不把p置为空指针,那么p就是野指针
    return 0;
}

🐰动态开辟常见的错误

可能开辟空间失败,然后再去解引用会发生错误,所以malloc的返回值要去判断和越界访问。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
    int* p=(int*)malloc(20);
    //可能开辟空间失败,然后再去解引用会发生错误,所以malloc的返回值要去判断
    //所以我们需要判断
    if(p==NULL)
    {
        printf("%s",strerror(errno));//打印发生的错误
        return; 
    }
    for(int i=0;i<5;i++)
    {
        p[i]=i;
    }

    //越界访问
    for(int i=0;i<10;i++)//只开辟了5个int类型的空间,却访问了10个int类型的空间
    {
        p[i]=i;
    }

    free(p);
    p=NULL;
    return 0;
}

对非动态开辟的空间进行释放

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int arr[10]={1,2,3};
    int *p=arr;
    free(p);//对非动态开辟的空间进行释放
    return 0;
}

释放一部分动态开辟的空间

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int* p=(int*)malloc(40);
    for(int i=0;i<10;i++)
    {
        p[i]=i;
    }
    p++;//p指向的就不是动态开辟空间的首地址
    free(p);//使用free释放一块动态开辟内存的一部分,不是从开辟空间的首地址开始释放的
    p=NULL;
    return 0;
}

对一块空间多次释放

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int* p=(int*)malloc(40);
    free(p);
    //p=NULL;//如果释放了指针之后,再次释放是不会出错的,因为指针为空时,free不会做出任何反应
    free(p);//重复释放
    p=NULL;
    return 0;
}

不能对空指针进行解引用操作

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void GetMemory(char* str)
{
    str=(char *)malloc(20);
    //str是p的临时拷贝,malloc动态开辟的空间,将首地址给了str,p仍然是空指针
}
int main()
{
    char* p=NULL;
    GetMemory(p);
    strcpy(p,"hello world");//这里对空指针进行了解引用操作
    printf("%s",p);
    return 0;
}

野指针问题

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* GetMemory(void)
{
    char str[]="hello world";
    //返回栈区空间的问题
    //GetMemory函数内部创建的数组str,str是临时创建,虽然返回了str数组的首地址,但是离开GetMemory函数之后,str内存会归还给系统,p的值虽然还是str数组的首地址,但是str空间已经归还给系统,str再去访问就是非法访问了。(栈区开辟的空间,离开作用域,栈区开辟的空间会被销毁)
    return str;
}
int main()
{
    char* p=NULL;
    p=GetMemory();
    printf("%s",p);
    return 0;
}

注:一定要记住

栈区:局部变量,函数形参

堆区:动态管理的空间(malloc,realloc,calloc,free)

静态区:静态变量,全局变量

 🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

相关文章:

  • 【分布式版本控制系统Git】| IDEA 集成 Git 、IDEA 集成 GitHub
  • C语言指针链表
  • 全网最完整,接口测试总结彻底打通接口自动化大门,看这篇就够了......
  • 【JVM虚拟机面试宝典】JVM的内存结构是怎么样的?在JVM中会发生内存溢出的区域有那些?— day06
  • C++ string类
  • 细数那些惊艳一时的 CSS 属性
  • 【C语言】你真的了解结构体吗
  • 可做题2(矩阵快速幂,乘法逆元,exgcd)
  • Mysql用户权限分配详解
  • 一文7个步骤从0到1教你搭建Selenium 自动化测试环境
  • 【网络安全工程师】从零基础到进阶,看这一篇就够了
  • 【C陷阱与缺陷】----语法陷阱
  • 解忧杂货铺(五续集):用了无法离开的网站资源
  • 功能测试转型测试开发年薪27W,又一名功能测试摆脱点点点,进了大厂
  • iOS 紧急通知
  • Create React App 使用
  • Git学习与使用心得(1)—— 初始化
  • iOS 系统授权开发
  • node和express搭建代理服务器(源码)
  • Python十分钟制作属于你自己的个性logo
  • Vue2.x学习三:事件处理生命周期钩子
  • WePY 在小程序性能调优上做出的探究
  • Windows Containers 大冒险: 容器网络
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 使用权重正则化较少模型过拟合
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 译米田引理
  • 由插件封装引出的一丢丢思考
  • kubernetes资源对象--ingress
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • (2)nginx 安装、启停
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (二)WCF的Binding模型
  • (二)学习JVM —— 垃圾回收机制
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • .NET运行机制
  • /dev/VolGroup00/LogVol00:unexpected inconsistency;run fsck manually
  • @selector(..)警告提示
  • [52PJ] Java面向对象笔记(转自52 1510988116)
  • [BetterExplained]书写是为了更好的思考(转载)
  • [BZOJ1060][ZJOI2007]时态同步 树形dp
  • [Linux]history 显示命令执行的时间
  • [Redis]Redis高级特性的配置及使用
  • [UVa11292] Dragon of Loowater
  • [week3]每周总结与工作计划
  • [win7-oracle处理方法]--java.lang.Exception: Exception in sending Request :: null(转)
  • [创业] 美国硅谷风险投资行业的详细报告(2008年第四季度)