动态内存管理——详解,彻底理解动态内存管理

目录

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

2.malloc 和 free(引用stdlib.h)

        1.malloc

        2.free

3.calloc 和 realloc(引用stdlib.h)

        1.calloc

        2.realloc

4.常见的动态内存管理错误

5.柔性数组

        1.什么是柔性数组:

        2.柔型数组的特点:

                1.柔型数组成员前必须至少有一个成员。

                2.sizeof求结构体的大小时,不包含柔型数组的大小

                3.在动态分配内存空间时,分配的内存空间必须大于结构体的大小,以适应柔型数组大小。

        3.柔性数组的使用

7.C/C++程序在内存中的存储区域划分


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

int value = 20; //vlaue值为20,在内存中占了4个字节。

int value[10] = {0}; //数组value一共有10个元素,每个元素值为0,
                     // 数组value在内存中占40个字节。

        通过上面例子我们可以总结出一下几点:

                1.在声明变量是,内存空间已经开辟好了,并且开辟的是固定值

                2.在声明数组的时候,必须指定数组的长度(即数组的元素个数),并且之后不能改变数组的长度。

        那么,这就可能会造成内存的浪费。举个例子:你是36码的脚,但是店老板只卖你40码的鞋,那这双鞋能穿吗?理论上当然是能穿的。但是,这么做肯定会不合脚,有可能40码脚的人都没鞋穿。

        所以,在C语言中引入了动态内存分配的概念,程序员可以自己按照需求申请和释放空间,这给我们带来了很大的灵活性。

2.malloc 和 free(引用stdlib.h)

        1.malloc

                C语言中提供了动态内存分配的函数:

void* malloc(size_t size)

               在C语言中,我们通过malloc函数,可以在堆区申请一块空间供我们使用。通过上面代码,我们分析一下,malloc怎么使用,malloc函数向内存申请一块 连续可用 的空间,size 表示我们要开辟多大的一块内存空间(单位是字节)。

#include 
#include 
#include 
int main()
{
    int* p = (int*)malloc(40);
    if(p == NULL)
    {
        printf("%s\n",strerror(errno));
        return 1;
    }
}

注意:

        1.当malloc成功申请到一块内存空间时,则返回该空间的起始地址。

        2.当malloc没有申请到一块内存地址时,则返回空指针,所以我们必须在申请后进行检查。

        3.返回类型是void*,所以malloc不知道开辟空间的类型,这个需要使用者根据需要自己定义

        4.如果 size 为0,这个是C语言标准没有定义的,是根据编译器情况而定的。                     

        2.free

                free函数是专门用来释放我们申请的内存空间的。

void free(void* ptr)

注意:               

        1.ptr是我们申请的内存空间的起始地址。

        2.如果ptr不是指向我们所开辟得内存空间,这是free函数未定义的,编译不会通过。

        3.如果ptr是个空指针,那么free函数什么也不会做。

举个例子:

#include 
int main()
{
    int i = 0;
    int* p =(int*)malloc(40);
    if(p == NULL)    //检查p是否为空指针
    {
        return 1;   //如果是空指针,直接结束程序    
    }
    for(i = 0 ; i < 10 ; i++)
    {
        *(p+i)=i+1;    //赋值
    }
     for(i = 0 ; i < 10 ; i++)
    {
        printf("%d ",*(p+i));     //打印 1 2 3 4 5 6 7 8  9 10
    }
    return 0;
}

3.calloc 和 realloc(引用stdlib.h)

        1.calloc

                calloc和malloc作用都是开辟一块内存空间。

void* calloc(size_t num,size_t size);

                从名字上看,calloc 和 malloc其实相差不多,代码上也只是比malloc多了个num,那么它和malloc有什么区别呢。      

                1.calloc会在内存中开辟num个大小为size的元素空间,并把空间的每个字节初始化为0

                2.calloc 和 malloc的区别就是,calloc会在返回地址前,把空间全部初始化为0

                这样看,其实calloc就是malloc的孪生兄弟罢了,只不过比malloc多了一项初始化的功能。想不想要初始化,这取决于程序员本身。但是,要注意的是,calloc的初始化,是把空间的每个字节初始化为0,且只能为0。

        2.realloc

                realloc作用就是扩容。

void* realloc(void* ptr,size_t size)

                1.ptr是要调整的内存空间的地址

                2.size是调整后的空间大小

                3.realloc调整可能面对下面两种情况

                        a.内存空间后面还有足够大的空间,那么内存地址不变,内存空间变大。

                        b.内存空间后面没有足够大的空间,那么会找一个新的足够大的内存地址,将原内                             内存空间的内容复制拷贝到新的内存地址空间,内存地址改变。

动态内存管理——详解,彻底理解动态内存管理_第1张图片

4.常见的动态内存管理错误

        1.对NULL指针(空指针)解引用操作

                

int* p =(int*)malloc(4);    
     *p = 20;    //如果p是NULL指针,就会有问题
     free(p);

        2.对非动态内存分配的空间进行free释放

int a = 20;
int* p = &a;
free(p); //如果释放非动态分配内存空间,就会有问题

        3.free释放了动态分配内存空间的一部分

#include 
int main()
{
    int i = 0;
    int* p =(int*)malloc(40);
    if(p == NULL)    //检查p是否为空指针
    {
        return 1;   //如果是空指针,直接结束程序    
    }
    for(i = 0 ; i < 10 ; i++)
    {
        *p = i+1;
        p++;
    }
    free(p); //此时p的存储的已经不是动态内存分配空间的起始地址,且会造成动态内存分配的越界访问
    return 0;
}

        4.对动态内存空间的越界访问

#include 
int main()
{
    int i = 0;
    int* p =(int*)malloc(40);
    if(p == NULL)    //检查p是否为空指针
    {
        return 1;   //如果是空指针,直接结束程序    
    }
    for(i = 0 ; i < 10 ; i++)
    {
        *p = i+1;
        p++;            // i = 10 时,就会是造成越界访问
    }
    return 0;
}

        5.对同一块动态分配内存空间多次释放

int* p =(int*)malloc(4);    
     *p = 20;   
     free(p);    //frees释放后,p为野指针
     free(p);    //若对野指针进行释放,会出问题

        6.动态分配的内存空间没有free释放(内存泄漏)

#include 

void test()
{
    int* a = (int*)malloc(4);
    ...
    if(flag == 1)
    {
       return;      //若满足条件,跳出函数
    }
    ...
    free(a);     //如果满足条件,那么空间将永远不会得到释放,造成内存泄漏
}

int main()
{
    test();
    return 0;
}

        如果我们申请了空间,却不归还,就会造成内存泄漏,倘若我们总是申请却不归还,我们就有可能造成系统的崩溃,导致死机。所以,现在我们就要养成良好的代码书写习惯,有借有还。

5.柔性数组

        1.什么是柔性数组:

                在结构体中,最后一个元素是元素数未知大小的数组,就被称为柔型数组成员

        2.柔型数组的特点:

                1.柔型数组成员前必须至少有一个成员。

struct test
{
    int a;    //柔性数组前必须至少有一个成员变量
    int arr[]; //arr就是柔性数组
};

                2.sizeof求结构体的大小时,不包含柔型数组的大小

sizeof(struct test);    //输出的是 4

                3.在动态分配内存空间时,分配的内存空间必须大于结构体的大小,以适应柔型数组大小。

struct test* p = (struct test*)malloc(sizeof(struct test)+10*sizeof(int))
    // 这样我们就得到了10个int类型元素大小的柔性数组

        3.柔性数组的使用

#include 

struct test
{
    int a;    
    int arr[]; 
};

int main()
{
    int i = 0;
    struct test* p = (struct test*)malloc(sizeof(struct test)+10*sizeof(int));
    p.a = 20;
    for(i = 0 ; i < 10 ; i++)
    {
        p->arr[i] = i+1;
    }
    free(p);
    return 0;
}

7.C/C++程序在内存中的存储区域划分

        动态内存管理——详解,彻底理解动态内存管理_第2张图片

你可能感兴趣的:(C语言语法详解,数据结构,算法,c++,c语言)