c语言详解动态内存分配及常见错误的解决

 更新时间:2022年04月22日 14:32:36   作者:平凡的人1  
给数组分配多大的内存空间?你是否和初学C时的我一样,有过这样的疑问。这一期就来聊一聊动态内存的分配,读完这篇文章,你可能对内存的分配有一个更好的理解

为什么会有动态内存分配

内存使用方式有两种

1.创建一个变量

2.创建一个数组

int a = 1;
int arr[10];

但是这两种方式都有一些特点

1.空间是固定大小的,不会变

2.必须提前知道要开辟多少空间,必须指定数组的长度。

但是实际上,对于空间的需求,有些时候,我们并不知道要分配多少内存空间,有时候需要在程序运行时才知道,比如用一个数组存放班级学生的年龄,我们不知道有多少个学生,不知道需要多大的空间,而且编译器又不支持变长的数组,应该是常量表达式。这时候,用数组分配固定的空间就不适合了。

为了更好地分配内存空间,这时候就需要我们的动态内存分配了。

说到内存,我们知道内存分为3个区域,栈区,堆区,静态区。

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

堆区——动态内存分配

静态区——全局变量,静态变量(如static int a = 10)

动态内存函数的介绍

malloc

void *malloc( size_t size );

函数向堆区开辟一块连续的空间,返回空间的起始地址。

  • 开辟成功,返回一个开辟好的空间的指针
  • 开辟失败,返回NULL指针,所以要检查是否开辟成功
  • 返回的类型是void*,所以malloc并不知道开辟的类型,自己决定
  • 如果size为0,malloc的行为是标准未定义的,取决于编译器
  • 记得引用头文件#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
    int* p = (int*)malloc(10 * sizeof(int));
    if (p == NULL)
    {
        //打印错误原因的一个方式
        printf("%s\n", strerror(errno));
    }
    else
    {
        //正常使用
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            *(p + i) = i;
        }
        for (i = 0; i < 10; i++)
        {
            printf("%d ", *(p+i));
        }
    }
    //释放
    free(p);
    p = NULL;
    return 0;
}

 free

void free( void *memblock );

c语言提供一个函数free,专门用来动态内存的回收和释放的 

头文件是#include <stdlib.h>

注意几点:

用于动态开辟的内存,如果不是的话,那free函数行为是未定义的

如果指针是NULL指针,则函数什么事都不做

被free后的指针最好手动置为NULL,避免野指针,造成错误

free(p);
p=NULL;

calloc

void *calloc( size_t num, size_t size );

calloc也是用来动态开辟空间。头文件也为#include <stdlib.h>

与malloc的区别:

参数不同

与malloc的区别只在于calloc会返回地址之前把申请的空间的每个字节初始化为0。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
    //malloc(10*sizeof(int));
    int*p = (int*)calloc(10, sizeof(int));
    if (p == NULL)
    {
        printf("%s\n", strerror(errno));
    }
    else
    {
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            printf("%d ", *(p + i));
        }
    }
    free(p);
    p = NULL;
    return 0;
}

 调试一下,可以看到内存都初始化为0了

根据实际情况,如果需要初始化的话那就使用calloc

realloc

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

头文件也是引用#include <stdlib.h>

ptr是要调整的内存地址

size调整之后新大小,而不是增加的大小

返回值为调整之后的内存起始位置

realloc函数的出现让动态内存管理更加灵活

有时候我们发现申请的空间太小,有时候又会觉得申请过大了,为了合理的分配内存,我们可以对内存的大小做灵活的调整,realloc可以做到对动态内存大小的调整

注意事项

realloc在调整内存存在两种情况

情况1:原有空间之后有足够大的空间

  • 如果p指向的空间之后有足够的内存可以追加,则直接追加,后返回p

情况2:原有空间之后没有足够的空间

  • 如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟的内存空间地址
  • 得用一个新的变量来接收realloc的返回值

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
    int* p = (int*)malloc(20);
    if (p == NULL)
    {
        printf("%s\n", strerror(errno));
    }
    else
    {
        int i = 0;
        for (i = 0; i < 5; i++)
        {
            *(p + i) = i;
        }
    }
    //在使用malloc开辟的20个空间
    //假设这里20个字节不能满足我们的使用
    //希望我们能有40个字节的空间
    //这里就可以使用realloc来调整动态开辟的内存
    int* ptr = realloc(p, 40);
    if (ptr != NULL)
    {
        p = ptr; 
        int i = 0;
        for (i = 5; i < 10; i++)
        {
            *(p + i) = i;
        }
        for (i = 0; i < 10; i++)
        {
            printf("%d ", *(p+ i));
        }
    }
    free(p);
    p = NULL;
    return 0;
}

常见的错误

对NULL指针的解引用操作

int main()
{
    int* p = (int*)malloc(INT_MAX/4);
    *p = 20;//如果p是空指针,就会有问题;
    free(p);
    return 0;
}

越界访问

int* p = (int*)malloc(40);
    if (p == NULL)
    {
        return 0;
    }
    int i = 0;
    //越界
    for (i = 0; i <= 10; i++)
    {
        *(p + i) = i;
    }
    free(p);
    p = NULL;

应该是i<10

对非动态内存进行free

 int a = 10;
    int* p = &a;
    free(p);
    p = NULL;

使用free释放动态开辟内存的一部分

int* p = (int*)malloc(40);
    if (p == NULL)
    {
        return 0;
    }
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *p++ = i;
    }
    free(p);
    return 0;

 p不在指向动态内存开辟的起始位置,我们应该重新定义一个指针来记住起始地址

对同一块动态内存多次释放

int* p = (int*)malloc(40);
    if (p == NULL)
    {
        return 0;
    }
    free(p);
    free(p);

对动态内存内存忘记释放(内存泄漏)

忘记释放动态开辟的内存,就会造成内存泄漏

 while (1)
    {
        malloc(1);
        Sleep(1000);
    }

申请内存而不还,计算机的内存是有限的,内存将会被耗干

到此这篇关于c语言详解动态内存分配及常见错误的解决的文章就介绍到这了,更多相关c语言动态内存分配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++使用动态内存分配的原因解说

    C++使用动态内存分配的原因解说

    这篇文章主要介绍了C++使用动态内存分配的原因解说,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • QT输入框输入限制整理(正则表达式限制)

    QT输入框输入限制整理(正则表达式限制)

    我们有时需要限制文本框输入内容的类型,下面这篇文章主要给大家介绍了关于QT输入框输入限制的相关资料,文中通过代码介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-04-04
  • C语言中实现自定义数据类型的输入输出的方法和技巧

    C语言中实现自定义数据类型的输入输出的方法和技巧

    在 C 语言中,除了基本的数据类型(如整型、浮点型、字符型等),我们还经常需要自定义数据类型来满足特定的编程需求,所以本文给大家介绍了C语言中实现自定义数据类型的输入输出的方法和技巧,需要的朋友可以参考下
    2024-07-07
  • C语言安全之数组长度与指针实例解析

    C语言安全之数组长度与指针实例解析

    这篇文章主要介绍了C语言安全之数组长度与指针,需要的朋友可以参考下
    2014-07-07
  • c语言同名标靶点自动匹配算法实现实例代码

    c语言同名标靶点自动匹配算法实现实例代码

    这篇文章主要介绍了c语言同名标靶点自动匹配算法实现实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Matlab实现二维散点主方向直方图的绘制详解

    Matlab实现二维散点主方向直方图的绘制详解

    这篇文章主要为大家详细介绍了如何利用Matlab实现二维散点主方向直方图的绘制,文中的示例代码讲解详细,对我们学习Matlab有一定帮助,需要的可以参考一下
    2022-09-09
  • C++ 类模板、函数模板全特化、偏特化的使用

    C++ 类模板、函数模板全特化、偏特化的使用

    这篇文章主要介绍了C++ 类模板、函数模板全特化、偏特化的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • 深入理解链表的各类操作详解

    深入理解链表的各类操作详解

    本篇文章是对链表的各类操作进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言数据结构之图书借阅系统

    C语言数据结构之图书借阅系统

    这篇文章主要为大家详细介绍了C语言数据结构之图书借阅系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 详解C语言中双指针算法的使用

    详解C语言中双指针算法的使用

    双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。本文将通过示例带大家深入了解双指针算法的使用
    2022-08-08

最新评论