UltraDebug

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: A C D R G Y M Z X S P
公益项目,接受捐赠
查看: 1185|回复: 0
收起左侧

[C] C语言动态内存分配

[复制链接]
Pojieba

主题

0

回帖

UD

新手上路

UID
16
积分
31
注册时间
2022-1-30
最后登录
1970-1-1
2022-1-30 13:59:57 | 显示全部楼层 |阅读模式
前言
给数组分配多大的空间?
你是否和初学C时的我一样,有过这样的疑问。
这一期博客就来聊一聊动态内存的分配
读完这篇文章,你可能对内存的分配有一个更好的理解

动态内存分配的定义
首先我们要搞清楚什么是动态内存的分配
平常我们定义的数组,都是在栈区分配的空间,都是分配的空间都是固定的大小
这种分配固定大小的内存分配方法称之为静态内存分配
与静态内存相对的,就是可以控制内存的分配的动态内存分配
注意:这里动态内存分配的空间是在堆区申请的,不是在栈区申请的

这里要讲一下什么是栈区,什么是堆区
内存的空间并不是都是一样的,在学习C语言时,提到的区域大致上分为栈区,堆区,和静态区。就比如说在一个车间一样,不同的区域做着不同的事,就有不同的功能,但是这些不同的功能又不是毫不相关的,他们彼此联系,相互构成整个内存空间.

C语言动态内存分配 - Pojieba_UltraDebug

动态内存的优势

<1>  可以控制内存的大小
在很多时候,我们申请的空间是未知的
就比如说通讯录,在刚刚开始用的时候很小的空间就足够了,但是在未来你不知道你需要存下多少个号码,这时候就存在一个问题,你定的空间需要多少个字节,当申请的太少,就会出现存不下去的情况,如果存的空间过大,有会造成一定的浪费。
在动态内存分配就可以避免这个问题,你可以运用 reallac 控制大小,当内存达到申请的空间时,就会主动扩容,也就是再次向内存申请空间。

<2> 可以多次利用这部分空间
静态内存分配利用的空间,离开了作用域就会释放给系统
而动态内存分配的空间,只能在函数运行结束后由系统自动释放,需要用户主动去释放,可以通过利用完(就比如说打印元素,打印完),用户再通过 free函数释放 这块申请的空间,当再次用动态内存申请空间时,就可以再次利用这块空间,这样也能在一定程度上,可以节省一定的空间。

<3>不占用栈区的内存
假设栈区定义了变量
而每个变量分配内存时,之间又有一定的间隙
当定义的变量足够多时,空隙也会很多
这时候向系统申请一个比较大且连续的空间时,虽然有足够的空间,但是缺少了连续的空间
就无法申请到这部分空间
所以动态内存在堆区申请,就完全不必担心栈区的空间不够的问题
说到这里,你是不是有一个疑惑,为什么空间的内存存在栈区和堆区之分
如果感兴趣,可以参考这个回答——为什么存在栈区堆区

malloc calloc realloc和free函数的介绍
在动态内存的分配中,离不开malloc与calloc,这两个函数都是向内存申请空间

calloc
头文件 #include <stdlib.h>
格式 void *calloc(size_t num, size_t size);
功能 为num个大小为size字节的对象分配存储空间,该空间内的所有位都会初始化为。
返回值 若分配成功,则返回一个指向已分配的空间开头的指针;若分配失败,则返回空指针

malloc
头文件#include <stdlib.h>
格式 void *malloc(size_t size);
功能 为大小为size字节的对象分配存储空间,此存储空间中的初始值不确定
返回值 若分配成功,则返回一个指向已分配的空间开头的指针;若分配失败,则返回空指针

这两个函数都是向系统申请动态内存空间,他们的头文件,返回值和功能大致都是相同的
不同的是calloc函数开辟的空间,就会将空间的内容全部初始话为零
而,malloc函数向系统申请的空间,空间的值都是随机的

realloc
头文件#include <stdlib.h>
格式void *realloc(void *mem_address, unsigned int newsize);
功能先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
返回值如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

free
头文件#include <stdlib.h>
格式void free(void *ptr);
功能释放ptr指向的空间,让这部分空间能继续用于之后的动态分配。当ptr为空指针时,不执行任何操作。除此之外,当实际参数与之前通过calloc函数、mal1oc函数或realloc函数返回的指针不一致时,或者ptr指向的空间已经通过调用 free或real1oc被释放时,则作未定义处理
返回值

动态空间的申请与释放
讲完动态内存申请的相关函数,那具体的代码实现是什么呢
[XML] 纯文本查看 复制代码
<1>    double *x;
<2>    x=calloc(1,sizeof(double))或者x=malloc(sizeof(double));
<3>    free;
SyntaxHighlighter Copyright 2004-2013 Alex Gorbatchev.
C语言动态内存分配 - Pojieba_UltraDebug
下面动态分配的内存赋值并显示

为单个对象分配空间
[C] 纯文本查看 复制代码
#include<stdio.h>
#include<stdlib.h>
int main()//动态内存的赋值与显示
{
        int* a;
        a = malloc(sizeof(int));    //分配动态内存
        if (a == NULL)              //是否成功分配了储存空间,否则返回分配失败
                printf("分配失败");
        else
        {
                *a = 20;
                printf("*a=%d\n", *a);
                free(a);                //释放
        }
        return 0;
}
SyntaxHighlighter Copyright 2004-2013 Alex Gorbatchev.
为数组分配空间
[C] 纯文本查看 复制代码
#include<stdio.h>
#include<stdlib.h>
int main()//动态内存的赋值与显示
{
        int n = 0;  int* a; int i = 0;
        printf("输入分配空间元素的个数:>");
        scanf_s("%d", &n);
        a =(int *) calloc(n,sizeof(int));
        if (a == NULL)
                printf("分配失败");
        else
        {
                for (i = 0; i < n; i++)
                {
                        *(a + i) = i;
                        printf("*a=%d\n", *(a+i));
                }
                free(a);
        }
        return 0;
}
SyntaxHighlighter Copyright 2004-2013 Alex Gorbatchev.
这里其实没有“为数组开辟的空间”这一说
因为动态申请的空间都是一个一个的“块”
不难发现,calloc与malloc的差别并不大,只有第一个参数不同
在这两行代码中,存在着一个小细节
a = malloc(sizeof(int));
a =(int *) calloc(n,sizeof(int));
这两者的差别不仅仅是函数的不同,其中后者有强制类型转换,而前者没有
实际上在C语言的标准上,有无强制类型转换都是行得通的
因为无论是calloc还是malloc,他们的返回值都是void* ,这里的void*实际上可以转换为int*类型或者其他类型,换句话说,就是返回的指针是兼容所有类型的万能指针。
即指向void型的指针可以指向任意类型的对象,是一种特殊类型的指针。
指向void型的指针的值可以赋给指向任意类型的指针,反之亦可。

改变申请的动态内存(realloc的使用)
[C] 纯文本查看 复制代码
#include<stdlib.h>
#include<stdio.h>
int main()
{
        int* a; int i = 0;
        a=(int *)calloc(10,sizeof(int));
        if (a == NULL)
                printf("分配失败");
     //使用
        else
        {
                for (i = 0; i < 10; i++)
                {
                        *(a + i) = i;
                        printf("*a=%d\n", *(a + i));
                }
        //需要扩容
                int* ret = realloc(a, 80);
                if (a != NULL)
                {
                        a = ret;
                }
                free(a);
        a=NULL;
        }
} 
SyntaxHighlighter Copyright 2004-2013 Alex Gorbatchev.

扩容不能直接就a=realloc(a, 80),需要中间引一个中间变量*ret

扩容可能有三种情况

情况一(在a的地址处,有空余的空间来扩容)

C语言动态内存分配 - Pojieba_UltraDebug

情况二 (在a的地址处,没有空余的空间来扩容,但是有其他的空间可存储扩容后的空间)

C语言动态内存分配 - Pojieba_UltraDebug

情况三(reallo调整空间失败)

C语言动态内存分配 - Pojieba_UltraDebug

在这三种情况中,第一种的地址不变
第二种会在一个新的地方申请足够大的地方,此时的地址不在是a原先的地址
第三种就扩容失败,就会导致扩容前申请的空间,也发生了改变,所以不能直接用a来重新赋值。

欢迎点赞收藏加关注,如若有问题可以提出来C语言动态内存分配 - Pojieba_UltraDebug
UltraDebug免责声明
✅以上内容均来自网友转发或原创,如存在侵权请发送到站方邮件9003554@qq.com处理。
✅The above content is forwarded or original by netizens. If there is infringement, please send the email to the destination 9003554@qq.com handle.
回复 打印

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|站点地图|UltraDebug ( 滇ICP备2022002049号-2 滇公网安备 53032102000034号)

GMT+8, 2025-6-18 07:21 , Processed in 0.031221 second(s), 12 queries , Redis On.

Powered by Discuz X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表