C语言笔记18

156 阅读3分钟

动态内存管理

申请内存空间

malloc函数(需要包含头文件stdlib.h)可以从内存中申请一段连续的内存空间 但不会对分配的空间 进行初始化
函数malloc的声明是void* malloc(size_t size);
参数为需要申请的内存空间大小
返回值为void* 类型的指针 申请成功则为申请的内存的首地址 若失败返回值为NULL
成功申请内存空间后 可将返回的指针转为任意类型的不超过这段内存空间大小的指针
转换时需要将void* 指针强制类型转换后再赋值给其他类型的指针

int *pInt = NULL;
pInt = (int * )malloc(sizeof(int));//强制类型转换
//判断malloc函数是否成功申请内存空间
if(pInt != NULL)
{
    //若不为NULL,再使用这个指针
    *pInt = 123;
    printf("%d",*pInt);
}

calloc函数(需要包含头文件stdlib.h) 与malloc不同的是 calloc返回之前对分配的空间进行初始化 设置成0
声明是void* calloc(unsigned int num, unsigned int size);

p = (int*)calloc(10, sizeof(int));//申请十个10个int大小的内存空间

释放内存空间

通过malloc函数申请内存空间并使用完成后 要使用free函数把这段内存空间释放 因为如果只申请不释放 程序占用的内存空间会越来越大 直到没有可分配的空间 因此对于不再使用的空间 应当及时释放
函数free的声明是void free (void* ptr);
通过malloc申请内存空间后,系统内记录了这段内存空间的首地址和空间大小 保存到已分配的内存空间列表中 并保证这段空间不会再分配给别的地方
需要释放这段内存空间时 将首地址(不能偏移)传入free函数 free函数将查找这个首地址是否在已分配的内存空间列表中 若存在 则根据列表中记录的首地址和空间大小 释放这段内存空间
释放后,这段内存空间可以再次分配给别的地方
free函数的参数是void* 类型的指针 可以接受任何类型的指针 因此可以直接将pint传递给free函数而无需转换

int *pInt = NULL;
pInt = (int * )malloc(sizeof(int) ) ;
if (pInt != NULL)
{
    *pInt = 123;
    printf("%d",*pInt);
    //释放内存空间
    free(pInt);
}

如果把申请的内存空间首地址存放到指针p中 而下一次新申请的内存空间首地址会覆盖掉上一次的首地址 由于没有保存内存空间的首地址 程序中将无法再通过任何方式使用或释放这些内存空间 这种现象被称作内存泄露
具有内存泄露问题的代码若长时间运行 会导致程序所占用的内存空间将会越来越大 直到没有可分配的空间 无法再成功申请内存空间为止

从函数中返回指针

malloc和free在程序结束前都是有效的 所以可以将申请的内存空间指针从函数中返回
下面这个例子用来记录数组

#include<stdio.h>
#include <stdlib.h>

int* func(int n)
{
    int* pArr = NULL;
    pArr = (int*)malloc(sizeof(int) * n);//强制类型转换
    if (pArr == NULL)
    {
        // 申请失败,直接返回NULL
        return pArr;
    }
    // 申请成功,给每个元素赋值
    for(int i = 0; i < n; i++)
        pArr[i] = i;
    return pArr;
}

int main()
{
    // 数组长度为n, n初始化为10
    int n = 10;
    // 获取数组首元素指针
    int * p = func(n); 
    if (p != NULL)
    {
        //通过首元素指针偏移访问所有数组元素
        for (int i = 0; i < n; i++)
            printf("%d ", p[i]);
        //使用完记得释放
        free(p);
    }
    return 0;
}

image.png

image.png

image.png