C语言程序设计——动态内存分配

一、动态内存分配定义

我们在之前的学习中已经明白了操作系统内存的使用方式。

内存分为栈区、堆区、静态区,栈区储存局部变量和函数的形式参数等,静态区存储全局变量和静态变量等。而堆区则被用于动态内存分配。

二、动态内存函数的介绍

1.malloc和free

void* malloc ( size_t size ) ;

这个函数向内存申请了一块连续可用的内存。

void free ( void* memblock ) ;

free函数用于做动态内存的释放与回收。

注意点:

1)如果开辟空间成功,则返回指向这块空间的指针。如果失败,就返回一个空指针。

2)返回的指针类型为void*类型,当使用时需要强制类型转换。

3)参数size为开辟空间的大小,单位为字节。

4)在使用动态内存时要养成良好习惯:

        ①判断空间是否成功开辟(判断接收返回值的指针是否为空);

        ②动态申请的空间不再使用时,通过free函数返还给操作系统;

        ③返还内存空间后需要把刚才使用的指针置为空,防止成为野指针;

5)如果free的参数指向的空间必须是动态内存开辟的。

6)当free的参数时NULL指针,则函数什么事情都不做。

#include 
#include 
#include 

int main()
{
    //向内存申请10个整型的空间
    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+1) = i;
        }
        for(i=0;i<10;i++)
        {
            printf("%d",*(p+i));
        }
    }

    //将空间释放并将指针置为空
    free(p);
    p = NULL;

    return 0;
}

2.calloc

 void* calloc ( size_t num , size_t size ) ;

该函数的功能是为num个大小为size的元素开辟一块空间(即开辟num*size大小的空间),并将空间中的每一个字节都初始化为0 。

#include 
#include 
#include 

int main()
{
    //向内存申请10个整型的空间
    //int* p = (int*)malloc(10*(sizeof(int));
    
    int* p = (int*)calloc(10,sizeof(int));

    if(p == NULL)
    {
        //打印错误原因
        printf("%s\n",strerror(errno));
    }
    else
    {
        //正常使用
        
        int i = 0;
        
        //calloc已完成空间内字节的初始化
        //for(i=0;i<10;i++)
        //{
        //    *(p+1) = i;
        //}

        for(i=0;i<10;i++)
        {
            printf("%d",*(p+i));
        }
    }

    //将空间释放并将指针置为空
    free(p);
    p = NULL;

    return 0;
}

3.realloc

realloc函数可以做到对开辟的内存大小的调整。

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

注意点:

1)realloc传递的是需要调整的内存地址和调整之后的大小,返回的是调整之后内存的起始位置。

2)realloc在调整大小的基础上会将原内存中的数据拷贝到新的空间内。

3)realloc在调整内存空间存在两种情况:

        ①原空间之后有足够大的空间:原申请空间的地址不变,在其后拓展,返回原申请空间地址;

        ②原空间之后没有足够大的空间:新建足够大的空间,将原申请空间释放,并将其中的数据复制至新建的空间内,返回新开辟的内存空间地址。

由于这两种情况的原因,需要一个新的变量来接收realloc函数的返回值,当用原指针变量接收时,会出现空指针赋值的情况,不仅新的申请空间申请失败,还会使得原申请空间丢失。

#include 
#include 
#include 

int main()
{
    //向内存申请10个整型的空间
    int* p = (int*)malloc(20);

    if(p == NULL)
    {
        //打印错误原因
        printf("%s\n",strerror(errno));
    }
    else
    {
        //正常使用
        int i = 0;
        for(i=0;i<5;i++)
        {
            *(p+1) = i;
        }

        //现在已经通过malloc开辟了20个字节的空间
        //现在需要将空间拓展为40个字节
        
        p2 = realloc(p,40);

        if(p2 == NULL)
        {
            printf("%s\n",strerror(erron));
        }
        else
        {
            p = p2;
            for(i=0;i<10;i++)
            {
                printf("%d",*(p+i));
            }
        }    
    }
    //将空间释放并将指针置为空
    free(p);
    p = NULL;

    return 0;
}

三、常见的动态内存错误

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

int main()
{
    int* p = (int*)malloc(INT_MAX);
    *p = 20;//p的值为NULL,报错
    free(p);
}

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

void test()
{
    int i = 0;
    int *p = (int*)malloc(5*sizeof(int));
    if(p == NULL)
    {
        return 0;
    }
    else
    {
        int i = 0;
        for(i=0;i<10;i++) //越界访问
        {
            *(p+i) = i;
        }
    }

    free(p);
    p = NULL;

    return 0;
}

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

void test()
{
    int a = 10;
    int* p = &a;
    free(p);    //释放非堆区内存
    p = NULL;
}

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

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

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

int main()
{
    int* p = (int*)malloc(40);
    if(p == NULL)
    {
        return 0;
    }
    
    free(p);
    //...
    free(p);
    //对同一块动态内存的多次释放
    return 0;
}

6.动态开辟内存忘记释放(内存泄漏)

int main()
{
    while(1)
    {
        malloc(1);
    }

    return 0;
}

你可能感兴趣的:(c语言)