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

简介: 【C语言进阶】动态内存管理(上)

1.为什么存在动态内存管理


       在我们之前的学习里,我们已经掌握了开辟内存的方式

int val = 20;//在栈空间开辟四个字节
char arr[10] = { 0 };//在栈空间上开辟10个字节的连续空间


但是上述的开辟空间的方式存在很大的局限性:

1.开辟的空间大小是固定的;

2.数组在申明的时候必须指定数组的长度,所需要的内存大小在编译时分配。


但是,对于空间的需求不止于此,有时候我们只有在程序运行起来以后才能知道所需空间大小,那数组的编译时开辟空间就不能满足了,因此我们引入了动态内存开辟的方法。


2.动态内存函数介绍


1.malloc

void*malloc(size_tsize);


malloc向内存申请一块连续可用的空间,并返回这块空间的指针。

  • 如果开辟成功,返回指向这块空间的指针
  • 如果开辟失败,返回NULL,因此使用malloc以后要做好检查
  • 返回值的类型是void*,使用前要强转
  • 如果参数size传的是0,这是C语言标准未定义的行为,由编译器决定

5da2fe5dcb84426cb321c3510b795093.png


2.calloc


c语言还提供了一个函数叫calloc,也用来动态内存分配

void*calloc(size_tnum,size_tsize);


是用来为开辟num个大小为size的空间,并且把空间内的每个字节初始化为0,与malloc的区别就是是否会初始化申请的内存空间。

119c0a38c1b44f57a2a3ed10eb14c11b.png


3.realloc


realloc函数的出现,让动态内存开辟更加灵活,有时候我们在使用动态开辟的空间,会发现空间小了或者大了。为了合理的使用内存,我们引入了realloc函数。


void*realloc(void*memblock,size_tsize);


  • 第一个参数是需要调整的内存地址
  • 第二个参数是调整后的新大小,
  • 返回值是调整后的内存的起始位置
  • realloc函数调整内存空间有两种情况
  • 情况一:原有空间之后有足够大的空间,此时realloc函数会在该空间后面直接追加空间,原有数据不受影响
  • 情况二:原有空间之后没有足够大的空间,此时realloc函数会再开辟一个足够大的空间,并将原有数据拷贝到新的空间,然后释放原有空间。

33867355382049a1b50f81b57ddfa07c.png


4.free


上述的三个动态内存开辟函数都是在堆区上开辟空间,那么空间使用后是需要释放的,否则就会造成内存泄漏,因此我们用free函数释放动态开辟的内存空间


void free( void *memblock );


  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。


3.常见的动态内存错误


1.对NULL指针的解引用操作

void test()
{
    int *p = (int *)malloc(INT_MAX/4);
    *p = 20;//如果p的值是NULL,就会有问题
    free(p);
}


上面的代码没有检验是否开辟成功,判断p是否为空指针,直接解引用,如果p的值为NULL,就会有问题


2.对动态开辟空间越界访问

void test()
{
    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);
}


我们只开辟了10个整形空间,当i = 10的时候,会访问到11个整形空间,就会越界访问。


3.对非动态开辟内存使用free释放

void test()
{
    int a = 10;
    int *p = &a;
    free(p);
}


变量a是在栈区开辟的,而动态内存是在堆区开辟的,因此变量a不能用free释放,这是未定义的行为。


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

void test()
{
    int *p = (int *)malloc(100);
    p++;
    free(p);//p不再指向动态内存的起始位置
}


此时,p已经不再指向动态开辟的内存的起始位置,不能用free释放。

相关文章
|
2天前
|
存储 编译器 数据库
【再识C进阶5(上)】详细介绍C语言文件操作——文件是用于存储数据
【再识C进阶5(上)】详细介绍C语言文件操作——文件是用于存储数据
|
7天前
|
存储 算法 C语言
C语言指针与二维数组在函数参数传递和动态内存管理中的应用
C语言指针与二维数组在函数参数传递和动态内存管理中的应用
16 0
|
7天前
|
存储 C语言
C语言变量的内存地址深入探究
C语言变量的内存地址深入探究
25 0
|
7天前
|
C语言
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(二)
我们可以通过创建并定义符号常量NUMBER,来作为判断是否胜利的标准。如三子棋中,令NUMBER为3,则这八个方向中有任意一个方向达成3子连珠,则连珠的这个棋子所代表的玩家获胜。
18 1
|
7天前
|
算法 C语言 C++
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(一)
三子棋游戏设计的核心是对二维数组的把握和运用。
19 1
|
7天前
|
编译器 C语言 C++
从C语言到C++_21(模板进阶+array)+相关笔试题(下)
从C语言到C++_21(模板进阶+array)+相关笔试题
21 2
|
7天前
|
编译器 C语言 C++
从C语言到C++_21(模板进阶+array)+相关笔试题(上)
从C语言到C++_21(模板进阶+array)+相关笔试题
20 0
|
8天前
|
存储 程序员 C语言
C语言(15)----动态内存讲解
C语言(15)----动态内存讲解
18 1
|
8天前
|
C语言
C语言(11)----内存函数
C语言(11)----内存函数
13 1