C语言进阶——动态内存管理(上)

C语言进阶——动态内存管理(上),第1张

<div class="htmledit_views" id="content_views">
<h2 id="%E2%80%8B%E7%BC%96%E8%BE%91" style="text-align:center;"><img alt="" height="125" src="https://img-blog.csdnimg.cn/d0e64ea2b64b415d910035e5096a805c.gif" width="1000"/></h2>
<blockquote>
<p>个人主页:_麦麦_</p>
<p>今日名言:“你若爱,生活哪里都可爱。你若恨,生活哪里都可恨。你若感恩,处处可感恩。你若成长,事事可成长。不是世界选择了你,是你选择了这个世界。既然无处可躲,不如傻乐。既然无处可逃,不如喜悦。既然没有净土,不如静心。既然没有如愿,不如释然。”</p>
<p>                                                                                                          ——丰子恺《豁然开朗》</p>
</blockquote>
<p><img alt="" height="562" src="https://img-blog.csdnimg.cn/caccdd576af04aad8634cfb1f042f564.webp" width="1000"/></p>
<p id="main-toc"><strong>目录</strong></p>
<p id="%E2%80%8B%E7%BC%96%E8%BE%91-toc" style="margin-left:0px;"><a href="#%E2%80%8B%E7%BC%96%E8%BE%91" rel="nofollow">​编辑</a></p>
<p id="-toc" style="margin-left:0px;"></p>
<p id="%E4%B8%80%E3%80%81%E5%89%8D%E8%A8%80-toc" style="margin-left:0px;"><a href="#%E4%B8%80%E3%80%81%E5%89%8D%E8%A8%80" rel="nofollow">一、前言</a></p>
<p id="%E4%BA%8C%E3%80%81%E6%AD%A3%E6%96%87-toc" style="margin-left:0px;"><a href="#%E4%BA%8C%E3%80%81%E6%AD%A3%E6%96%87" rel="nofollow">二、正文</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A01.%E5%86%85%E5%AD%98%E7%9A%84%E5%88%86%E5%B8%83-toc" style="margin-left:40px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A01.%E5%86%85%E5%AD%98%E7%9A%84%E5%88%86%E5%B8%83" rel="nofollow">        1.内存的分布</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A02.%E4%B8%BA%E4%BB%80%E4%B9%88%E5%AD%98%E5%9C%A8%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%BC%80%E8%BE%9F-toc" style="margin-left:40px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A02.%E4%B8%BA%E4%BB%80%E4%B9%88%E5%AD%98%E5%9C%A8%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%BC%80%E8%BE%9F" rel="nofollow">        2.为什么存在动态内存开辟</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%87%BD%E6%95%B0%E7%9A%84%E4%BB%8B%E7%BB%8D-toc" style="margin-left:40px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%87%BD%E6%95%B0%E7%9A%84%E4%BB%8B%E7%BB%8D" rel="nofollow">        3.动态内存函数的介绍</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.1%20malloc-toc" style="margin-left:80px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.1%20malloc" rel="nofollow">                 3.1 malloc</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.2free-toc" style="margin-left:80px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.2free" rel="nofollow">                 3.2free</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.3calloc-toc" style="margin-left:80px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.3calloc" rel="nofollow">                3.3calloc</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.4realloc-toc" style="margin-left:80px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.4realloc" rel="nofollow">                 3.4realloc</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.%E5%B8%B8%E8%A7%81%E7%9A%84%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E7%9A%84%E9%94%99%E8%AF%AF-toc" style="margin-left:40px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.%E5%B8%B8%E8%A7%81%E7%9A%84%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E7%9A%84%E9%94%99%E8%AF%AF" rel="nofollow">         4.常见的动态内存的错误</a></p>
<p id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.1%E5%AF%B9%E7%A9%BA%E6%8C%87%E9%92%88%E7%9A%84%E8%A7%A3%E5%BC%95%E7%94%A8%E6%93%8D%E4%BD%9C-toc" style="margin-left:80px;"><a href="#%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.1%E5%AF%B9%E7%A9%BA%E6%8C%87%E9%92%88%E7%9A%84%E8%A7%A3%E5%BC%95%E7%94%A8%E6%93%8D%E4%BD%9C" rel="nofollow">                4.1对空指针的解引用 *** 作</a></p>
<p id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.2%E5%AF%B9%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E7%A9%BA%E9%97%B4%E7%9A%84%E8%B6%8A%E7%95%8C%E8%AE%BF%E9%97%AE-toc" style="margin-left:80px;"><a href="#%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.2%E5%AF%B9%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E7%A9%BA%E9%97%B4%E7%9A%84%E8%B6%8A%E7%95%8C%E8%AE%BF%E9%97%AE" rel="nofollow">                4.2对动态开辟空间的越界访问</a></p>
<p id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.3%E5%AF%B9%E9%9D%9E%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E4%BD%BF%E7%94%A8free%E9%87%8A%E6%94%BE-toc" style="margin-left:80px;"><a href="#%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.3%E5%AF%B9%E9%9D%9E%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E4%BD%BF%E7%94%A8free%E9%87%8A%E6%94%BE" rel="nofollow">                4.3对非动态开辟内存使用free释放</a></p>
<p id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.4%E4%BD%BF%E7%94%A8free%E9%87%8A%E6%94%BE%E4%B8%80%E5%9D%97%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86-toc" style="margin-left:80px;"><a href="#%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.4%E4%BD%BF%E7%94%A8free%E9%87%8A%E6%94%BE%E4%B8%80%E5%9D%97%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86" rel="nofollow">                4.4使用free释放一块动态开辟内存的一部分</a></p>
<p id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.5%E5%AF%B9%E5%90%8C%E4%B8%80%E5%9D%97%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%A4%9A%E6%AC%A1%E9%87%8A%E6%94%BE-toc" style="margin-left:80px;"><a href="#%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.5%E5%AF%B9%E5%90%8C%E4%B8%80%E5%9D%97%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%A4%9A%E6%AC%A1%E9%87%8A%E6%94%BE" rel="nofollow">                4.5对同一块动态内存多次释放</a></p>
<p id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.6%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E5%BF%98%E8%AE%B0%E9%87%8A%E6%94%BE%EF%BC%88%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%EF%BC%89-toc" style="margin-left:80px;"><a href="#%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.6%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E5%BF%98%E8%AE%B0%E9%87%8A%E6%94%BE%EF%BC%88%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%EF%BC%89" rel="nofollow">                4.6动态开辟内存忘记释放(内存泄漏)</a></p>
<p id="%C2%A0%E4%B8%89%E3%80%81%E7%BB%93%E8%AF%AD-toc" style="margin-left:0px;"><a href="#%C2%A0%E4%B8%89%E3%80%81%E7%BB%93%E8%AF%AD" rel="nofollow"> 三、结语</a></p>
<hr id="hr-toc"/>
<p></p>
<h2 id="%E4%B8%80%E3%80%81%E5%89%8D%E8%A8%80">一、前言</h2>
<blockquote>
<p>  小伙伴们好呀,今天为大家带来的是动态内存的相关知识,主要围绕<span style="color:#fe2c24;"><strong>动态内存管理相关函数</strong></span><span style="color:#494949;">和</span><strong><span style="color:#fe2c24;">常见错误</span></strong>并伴有一定的题目练习,希望能够为读者们带来一定的收获。<img alt="" height="152" src="https://img-blog.csdnimg.cn/cb3ef087d3b2438786b3a3fb4b7ada50.gif" width="150"/></p>
</blockquote>
<h2 id="%E4%BA%8C%E3%80%81%E6%AD%A3%E6%96%87">二、正文</h2>
<h3 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A01.%E5%86%85%E5%AD%98%E7%9A%84%E5%88%86%E5%B8%83">        1.内存的分布</h3>
<blockquote>
<p>        在具体讲解动态内存管理之前,需要先给小伙伴们铺垫一下<strong><span style="color:#fe2c24;">内存空间的分布</span></strong>(简略版)。在之前的学习中,我们认识了局部变量,全局变量等,那它们在内存中处于何种位置呢?</p>
<p>        在内存中可以粗略地分为三大块:<strong><span style="color:#fe2c24;">栈区</span></strong>、<strong><span style="color:#fe2c24;">堆区</span></strong>和<strong><span style="color:#fe2c24;">静态区</span></strong>。栈区中存放的大多是一些<span style="background-color:#fbd4d0;">局部变量,函数的形式参数</span>;而我们下面所要学习的与<span style="color:#333333;"><span style="background-color:#ffd7b9;">动态内存管理相关的函数</span></span>则存在与堆区;最后的静态区则存放<span style="color:#333333;"><span style="background-color:#d4e9d5;">静态变量与全局变量</span></span>。</p>
</blockquote>
<p style="text-align:center;"><img alt="" height="1160" src="https://img-blog.csdnimg.cn/dc419248ca654630ab2b74890c87c93b.png" width="1000"> </img></p>
<h3 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A02.%E4%B8%BA%E4%BB%80%E4%B9%88%E5%AD%98%E5%9C%A8%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%BC%80%E8%BE%9F">        2.为什么存在动态内存开辟</h3>
<blockquote>
<p>         在了解完大致的内存分布后,接下来就是动态内存管理了。我们都知道无论是创建一个整形变量亦或是一个指定大小的数组,都是需要向内存申请相应大小的空间,也就是在内存里开辟空间。而目前我们已经掌握的内存开辟方式有:</p>
</blockquote>
<pre><p style='background-color:black;color:white;padding:12px;'>int val = 20;            //在栈空间上开辟四个字节
char arr[20] = { 0 };    //在栈空间上开辟10个字节的连续空间</p></pre>
<blockquote>
<p>        我们会上述的<strong><span style="color:#fe2c24;">开辟空间的方式</span></strong>有两个特点:</p>
<p><strong><span style="color:#333333;">1.空间开辟大小是固定的</span></strong></p>
<p><span style="color:#333333;"><strong>2.数组在申明的时候,必须指定数组的长度,它所需要的内存在编译的时候进行分配</strong></span></p>
<p>        但是对于空间的需求,不仅仅是上述的情况,有时候我们<span style="background-color:#ffd7b9;">所需要的空间大小在程序运行的时候才能知道</span>,那么采用数组的编译时就开辟空间的这种方式就不能满足这种需求。这个时候,就只能试试<strong><span style="color:#fe2c24;">动态内存开辟</span></strong>了。</p>
</blockquote>
<h3 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%87%BD%E6%95%B0%E7%9A%84%E4%BB%8B%E7%BB%8D">        3.动态内存函数的介绍</h3>
<blockquote>
<p>        而C语言正好提供了与动态内存开辟相关的函数,下面就即将展开相关函数的学习。</p>
</blockquote>
<h4 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.1%20malloc">                 3.1 <strong>malloc</strong></h4>
<pre><p style='background-color:black;color:white;padding:12px;'>void* malloc(size_t size);</p></pre>
<blockquote>
<p>        这个函数是<strong><span style="color:#fe2c24;">向内存申请一块连续可用的空间</span></strong>,并<strong><span style="color:#fe2c24;">返回指向这块空间的指针</span></strong></p>
<p>●引用的头文件:<span style="background-color:#c7e6ea;"><stdlib.h></span></p>
<p>●如果开辟成功,则返回一个指向开辟好空间的指针</p>
<p>●如果开辟失败,则返回一个指向NULL指针,因此<strong><span style="color:#fe2c24;">malloc的返回值一定要做检查</span></strong></p>
<p>●返回的类型是void*,所以malloc函数并不只知道<span style="background-color:#c7e6ea;">开辟空间的类型,具体在使用的时候由使用者自己来决定</span></p>
<p>●如果参数size为0,malloc的行为是标准未定义的,取决于编译器</p>
<p>        下面演示一下malloc函数的简单使用:</p>
</blockquote>
<pre><p style='background-color:black;color:white;padding:12px;'>//malloc函数使用示例
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
    //申请40个字节,用来存放10个整型
    int* p = (int*)malloc(40);
    if (p == NULL)
    {
        printf("%s\n", strerror(errno));
        return 1;
    }
    //存放1-10
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i + 1;
    }
    //打印
    for(i = 0; i < 10;i++)
    {
        printf("%d ", *(p + i));
    }
    return 0;
}</p></pre>
<p style="text-align:center;"><img alt="" src="https://img-blog.csdnimg.cn/58dce7c161274a76ae0d70080b2b211b.png"> </img></p>
<blockquote>
<p>注:当申请完空间后要再主动地返回空间,虽然程序结束 *** 作系统会自动回收,但是如果程序一直不结束,那么这部分空间就会一直被闲置,那么如何回收呢?</p>
</blockquote>
<h4 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.2free">                 3.2free</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>void free (void * ptr);</p></pre>
<blockquote>
<p>        C语言中提供了另外一个函数free,专门是用来做<strong><span style="color:#fe2c24;">动态内存的释放和回收</span></strong>的。free函数用来释放动态内存开辟的内存。</p>
<p>●如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义</p>
<p>●如果参数ptr是NULL指针,则函数什么事都不做</p>
</blockquote>
<pre><p style='background-color:black;color:white;padding:12px;'>#include <string.h>
int main()
{
    //申请40个字节,用来存放10个整型
    int* p = (int*)malloc(40);
    if (p == NULL)
    {
        printf("%s\n", strerror(errno));
        return 1;
    }
    //存放1-10
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i + 1;
    }
    //打印
    for(i = 0; i < 10;i++)
    {
        printf("%d ", *(p + i));
    }
    //free是释放申请的内存
    free(p);
    return 0;
}</p></pre>
<p style="text-align:center;"><img alt="" src="https://img-blog.csdnimg.cn/d4887aaa987945e98956fb1d79c94bbc.png"> </img></p>
<blockquote>
<p>        但是我们发现虽然我们已经用free释放了申请的空间,但是p仍存储着指向我们所申请的空间的地址,也就是存在非法访问的的风险,所以一般情况下,<strong><span style="color:#fe2c24;">当我们使用free释放动态内存之后,还要将指向那块空间的指针置空。</span></strong></p>
</blockquote>
<pre><p style='background-color:black;color:white;padding:12px;'>//malloc函数使用示例
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
    //申请40个字节,用来存放10个整型
    int* p = (int*)malloc(40);
    if (p == NULL)
    {
        printf("%s\n", strerror(errno));
        return 1;
    }
    //存放1-10
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i + 1;
    }
    //打印
    for(i = 0; i < 10;i++)
    {
        printf("%d ", *(p + i));
    }
    //free是释放申请的内存
    free(p);
    p = NULL;
    return 0;
}</p></pre>
<h4 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.3calloc">                3.3calloc</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>void * calloc(size_t num,size_t size);</p></pre>
<blockquote>
<p>        除了malloc之外,C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配</p>
<p>●calloc的功能是为num个大小为size的元素开辟空间,并且把<strong><span style="color:#fe2c24;">空间的每个字节初始化为0</span></strong></p>
<p>●calloc与malloc的区别只在于在calloc会在返回地址之前把申请的空间的每个字节初始化全0</p>
</blockquote>
<pre><p style='background-color:black;color:white;padding:12px;'>#include <stdlib.h>
int main()
{
    int* p = calloc(10, sizeof(int));
    if (NULL!=p)
    {
        //使用空间
    }
    free(p);
    p = NULL;
}</p></pre>
<p style="text-align:center;"><img alt="" height="377" src="https://img-blog.csdnimg.cn/9c28d8169e5f439f93cccdfd6a5e0cb5.png" width="1000"> </img></p>
<blockquote>
<p>         那么在开辟动态内存空间的时候在mallo和calloc两个函数中该如何选择呢?主要是根据自己的需求来考虑。</p>
<p>●malloc无需初始化直接返回地址——效率比较高</p>
<p>●calloc会自动初始化为0</p>
<p>     </p>
</blockquote>
<h4 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A03.4realloc">                 3.4realloc</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>void * realloc(void * ptr, size_t size);</p></pre>
<blockquote>
<p>●realloc函数的出现让动态内存管理更加灵活</p>
<p>●有时我们会发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的调整,那relloc函数就可以做到<strong><span style="color:#fe2c24;">对动态开辟内存大小的调整</span></strong></p>
<p>        下面具体来认识下realloc函数</p>
<p>●ptr是要调整的内存地址</p>
<p>●size为调整之后新的大小</p>
<p>●返回值为调整之后的内存其实位置</p>
<p>●这个函数在调整原内存大小的基础上,还会将原来内存中数据移动到新的空间</p>
<p>●ralloc在调整内存空间的时候存在<strong><span style="color:#fe2c24;">两种情况</span></strong>:</p>
<p>        (1)原有空间之后有足够大的空间</p>
<p>        (2)原有空间之后没有足够大的空间</p>
</blockquote>
<p style="text-align:center;"><img alt="" src="https://img-blog.csdnimg.cn/b5c8923da2b94db29e2ea2e005c90444.png"/> </p>
<blockquote>
<p>         那么realloc会如何处理这两种情况呢?</p>
<p>情况1:当是情况1的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化,<strong><span style="color:#fe2c24;">返回的是原有的起始地址</span></strong></p>
<p>情况2:当是情况2的时候,原有空间之后没有足够多的空间时,拓展的方法是:在对科技上另找一个合适大小的连续空间来使用,这样的话<span style="color:#fe2c24;"><strong>函数返回的是一个新的内存地址</strong></span></p>
<p>        由于上述的两种情况,当我们使用realloc函数就和之前有一些区别。那么具体该如何使用,具体代码如下:</p>
</blockquote>
<pre><p style='background-color:black;color:white;padding:12px;'>#include <stdlib.h>
#include <stdio.h>
#include <string.h>//realloc的使用方式1
int main()
{
    int* p = (int*)malloc(5 * sizeof(int));
    if (NULL == p)
    {
        perorr("malloc");
        return 1;
    }
    //使用
    int i = 0;
    for (i = 0; i < 5; i++)
    {
        *(p + i) = 1;
    }
    //不够了,增加5个整型的空间
    p=realloc(p, 10 * sizeof(int));
    //继续使用空间
    for (i = 0; i < 10; i++)
    {
        printf("%d ", *(p + i));
    }
    //释放空间
    free(p);
    p = NULL;
    return 0;
}//realloc的使用方式2
int main()
{
    int* p = (int*)malloc(5 * sizeof(int));
    if (NULL == p)
    {
        perorr("malloc");
        return 1;
    }
    //使用
    int i = 0;
    for (i = 0; i < 5; i++)
    {
        *(p + i) = 1;
    }
    //不够了,增加5个整型的空间
    int* ptr = realloc(p, 10 * sizeof(int));
    if (ptr != NULL)
    {
        p = ptr;
    }
    //继续使用空间
    for (i = 0; i < 10; i++)
    {
        printf("%d ", *(p + i));
    }
    //释放空间
    free(p);
    p = NULL;
    ptr = NULL;
    return 0;
}</p></pre>
<blockquote>
<p>         第一次使用的小伙伴在使用realloc函数的时候很有可能就是按照第一种方式来写的,而第一种方式和第二种方式的区别在于<strong><span style="color:#fe2c24;">对realloc函数返回的指针的接受方法不同</span></strong>。前者是<span style="background-color:#dad5e9;">直接用第一次开辟的空间的指针来接受</span>,而后者则是<span style="background-color:#dad5e9;">使用了一个新指针来接受返回的指针</span>。</p>
<p>        那么到底哪种方式更好呢,在认识到realloc函数返回的不同情况下,方式2是一种更好的情况。若是采取方式1,一旦realloc函数<span style="background-color:#fbd4d0;">扩容失败,就会返回一个空指针,而且会把原来的指针置空</span>,相当于把原来的<span style="background-color:#fbd4d0;">数据丢失</span>了,可以说是很危险的一种情况,而方式2恰恰避免了这种情况。</p>
</blockquote>
<h3 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.%E5%B8%B8%E8%A7%81%E7%9A%84%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E7%9A%84%E9%94%99%E8%AF%AF">         4.常见的动态内存的错误</h3>
<blockquote>
<p>        在掌握了动态内存函数的使用方法之后,可能有的小伙伴在后续的书写中还会遇到代码错误的问题,以下列举了在使用动态内存函数会出现的常见错误,来帮助大家更好的规避错误,写出优秀的代码。</p>
</blockquote>
<h4 id="%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.1%E5%AF%B9%E7%A9%BA%E6%8C%87%E9%92%88%E7%9A%84%E8%A7%A3%E5%BC%95%E7%94%A8%E6%93%8D%E4%BD%9C">                4.1对空指针的解引用 *** 作</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>//对NULL指针的解引用 *** 作
void text1()
{
    int* p = (int*)malloc(INT_MAX / 4);
    *p = 20;//如果p的值是NULL,就会有问题
    free(p);
}</p></pre>
<h4 id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.2%E5%AF%B9%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E7%A9%BA%E9%97%B4%E7%9A%84%E8%B6%8A%E7%95%8C%E8%AE%BF%E9%97%AE">                4.2对动态开辟空间的越界访问</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>//对动态开辟空间的越界访问
void text2()
{
    int i = 0;
    int* p = (int*)malloc(10 * sizeof(int));
    if (NULL == p)
    {
        exit(EXIT_FAILURE);
    }
    for (i = 0; i <= 10; i++)
    {
        *(p + i) = i;    //当i是10的时候越界访问
    }
    free(p);
    p = NULL;
}</p></pre>
<h4 id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.3%E5%AF%B9%E9%9D%9E%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E4%BD%BF%E7%94%A8free%E9%87%8A%E6%94%BE">                4.3对非动态开辟内存使用free释放</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>//对非动态开辟内存使用free释放
void text3()
{
    int a = 0;
    int* p = &a;    
    free(p);    //释放错误
}</p></pre>
<h4 id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.4%E4%BD%BF%E7%94%A8free%E9%87%8A%E6%94%BE%E4%B8%80%E5%9D%97%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86">                4.4使用free释放一块动态开辟内存的一部分</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>//使用free释放一块动态开辟内存的一部分
void text4()
{
    int* p = (int*)malloc(100);
    p++;
    free(p);    //p不在指向动态内存的起始位置
    p = NULL;
}</p></pre>
<h4 id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.5%E5%AF%B9%E5%90%8C%E4%B8%80%E5%9D%97%E5%8A%A8%E6%80%81%E5%86%85%E5%AD%98%E5%A4%9A%E6%AC%A1%E9%87%8A%E6%94%BE">                4.5对同一块动态内存多次释放</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>//对同一块动态内存多次释放
int main()
{
    int* p = (int*)malloc(100);
    if (NULL == p)
    {
        return 1;
    }
    //使用
    //释放
    free(p);
    //..    free(p);
    return 0;
}</p></pre>
<h4 id="%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A04.6%E5%8A%A8%E6%80%81%E5%BC%80%E8%BE%9F%E5%86%85%E5%AD%98%E5%BF%98%E8%AE%B0%E9%87%8A%E6%94%BE%EF%BC%88%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%EF%BC%89">                4.6动态开辟内存忘记释放(内存泄漏)</h4>
<pre><p style='background-color:black;color:white;padding:12px;'>//动态开辟内存忘记释放(内存泄漏)
void text()
{
    int* p = (int*)malloc(100);
    //使用
    //......
}
int main()
{
    text();
    //..
    return 0;
}</p></pre>
<blockquote>
<p>         在这种情况下,一旦第一次忘记释放,第二想再释放都释放不了,除非程序结束自动释放。那么有什么好的方法来避免上述情况吗?</p>
</blockquote>
<pre><p style='background-color:black;color:white;padding:12px;'>//第一层保障:malloc和free成对使用
void text()
{
    int* p = (int*)malloc(100);
    if (NULL == p)
    {
        return 1;
    }
    //使用
    free(p);
    p = NULL;
}int main()
{
    text();
    //....
    return 0;
}//第二层:将指向动态开辟内存的指针返回去,谁接受谁后面去释放,所以这种函数一定要写注释
int* text()
{
    int* p = (int*)malloc(100);
    if (NULL == p)
    {
        return 1;
    }
    //使用
    return p;
}</p></pre>
<h2 id="%C2%A0%E4%B8%89%E3%80%81%E7%BB%93%E8%AF%AD"> 三、结语</h2>
<blockquote>
<p>         到此为止,关于动态内存管理的讲解就已完成一部分了。</p>
<p>         关注我 <strong>_麦麦_</strong>分享更多干货:<a href="https://blog.csdn.net/m0_73953114?spm=1010.2135.3001.5343" title="_麦麦_的博客_CSDN博客-领域博主">_麦麦_的博客_CSDN博客-领域博主</a><br/>          大家的「<strong>关注❤️ + 点赞 + 收藏⭐</strong>」就是我创作的最大动力!谢谢大家的支持,我们下期见!</p>
<p><img alt="" height="100" src="https://img-blog.csdnimg.cn/7d22ec2429904baea8c6cebf06f6a7a0.gif" width="100"/></p>
<p> </p>
</blockquote>
</div>

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/13518900.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2024-05-29
下一篇 2024-06-02

发表评论

登录后才能评论

评论列表(0条)

保存