2021-07-03

个人博客网址:https://ljsblog.com

动态内存分配(十)

在不知道所需要的空间大小的情况下,这时就可以使用动态内存开辟。
当开辟的空间不再使用时,用free函数来释放calloc、malloc或realloc所分配的内存空间。

动态内存函数

free函数

free函数是用来释放动态开辟的空间
格式:
void free(void *ptr)
free函数的头文件: stdlib.h

malloc函数

malloc函数会在内存开辟一块连续可用的空间,并返回一个指向它的指针,若开辟失败,则返回NULL,所以malloc的返回值一定要检查。
格式:
void *malloc(size_t size)
头文件: stdlib.h
例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
    int i=0;
    //向内存申请10个整形空间
    //因为malloc的返回值的类型是void*,所以需要用(int*)将其转换化为int*类型
    int* p=(int*)malloc(10*sizeof(int));
    //检查开辟成功还是失败
    if(p==NULL)//判断指针是否为空,为空则代表开辟空间失败
    {
        //打印错误原因
        printf("%s\n",strerror(errno));
    }
    else
    {
        //赋值并打印
        for(i=0;i<10;i++)
        {
            *(p+i)=i;
        }
        for(i=0;i<10;i++)
        {
            printf("%d ",*(p+i));//0 1 2 3 4 5 6 7 8 9 
        }
    }
    //释放空间
    free(p);
    //此时,虽然空间被释放掉,但p依然指向那个地址,所以需要将p赋一个空指针
    p=NULL;
    return 0; 
}

calloc函数

calloc函数会在内存开辟一块连续可用的空间,并返回一个指向它的指针。
与malloc的不同是,calloc会把开辟的空间的每个字节初始化为零。
格式
void *calloc(size_t nitems, size_t size)
nitems:元素个数。
size:元素大小。
头文件: stdlib.h
例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
    int i=0;
    int* p=(int*)calloc(10,sizeof(int));
    //向内存申请10个大小为int的空间并将每个字节初始化为0
    if(p==NULL)
    {
        printf("%s\n",strerror(errno));
    }
    else
    {
        for(i=0;i<10;i++)
        {
            printf("%d ",*(p+i));//0 0 0 0 0 0 0 0 0 0
        }
    }
    free(p);
    p=NULL;
    return 0;
}

realloc函数

realloc函数可以调整动态开辟空间的大小。
使用realloc函数的注意事项:

  1. 原有空间后有足够大的空间,就直接在原有内存后追加空间,原来空间内容不发生变化。
  2. 原有空间后没有足够大的空间,则另找一个合适大小的连续空间来使用,并将原内存的数据移动到新的空间,释放旧的内存,函数返回的将是一个新空间的地址。
  3. 需要用一个新的指针变量来接受realloc函数的返回值。

格式:
void *realloc(void *ptr, size_t size)
ptr:之前开辟的内存块地址
size:调整后新大小
例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
    int i=0;
    int* p=(int*)malloc(20);
    //使用malloc开辟了20个字节空间
    int* p1=NULL;
    if(p==NULL)
    {
        printf("%s\n",strerror(errno));
    }
    else
    {
        for(i=0;i<5;i++)
        {
            *(p+i)=i;
        }
    }
    //希望有40个字节空间
    p1=(int*)realloc(p,40);
    //使用realloc调整内存空间为40个字节
    if(p1!=NULL)
    {
        p=p1;
        for(i=5;i<10;i++)
        {
            *(p+i)=i;
        }
        for(i=0;i<10;i++)
        {
            printf("%d ",*(p+i));//0 1 2 3 4 5 6 7 8 9
        }  
    }
    //释放内存
    free(p);
    p=NULL;
    return 0;
}

常见的动态内存错误

1.对NULL指针解引用操作
错误示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int* p=(int*)malloc(40);
    //有可能malloc开辟空间失败返回的NULL
    *p=10;
    return 0;
}

2.对动态开辟空间的越界访问
错误示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int i=0;
    int* p=(int*)malloc(10*sizeof(int));//10个int
    if(p==NULL)
    {
        return 0;
    }
    for(i=0;i<=10;i++)//越界
    {
        *(p+i)=i;
    }
    free(p);
    p=NULL;
    return 0;
}

3.对非动态开辟的内存使用free释放
错误示例:

#include <stdio.h>
#include <stdilb.h>
int main()
{
    int a=10;
    int* p=&a;
    free(p);//对非动态开辟的内存使用free释放
    p=NULL;
    return 0;
}

4.使用free函数释放动态开辟内存的一部分
错误示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int i=0;
    int* p=(int*)malloc(10*sizeof(int));
    if(p==NULL)
    {
        return 0;
    }
    for(i=0;i<10;i++)
    {
        *p++=i;
    }
    //p已经不在原来的位置了
    free(p);
    p=NULL;
    return 0;
}

5.对同一块动态内存多次释放
错误示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int i=0;
    int* p=(int*)malloc(10*sizeof(int));
    if(p==NULL)
    {
        return 0;
    }
    free(p);
    free(p);
    return 0;
}

6.忘记释放动态开辟内存
错误示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    while(1)
    {
        malloc(1);
    }
    return 0;
}

柔性数组(C99)

在结构体内,结构体中的最后一个元素可以是未知大小的数组,而且有特定的形式[]或者[0],叫做柔型数组成员

  • 结构体中柔性数组成员前面必须至少有一个成员
  • 在计算结构体大小时,不包含柔性数组成员
#include <stdio.h>
#include <stdlib.h>
struct S
{
    int n;
    int arr[];
};
int main()
{
    int i=0;
    struct S* p=(struct S*)malloc(sizeof(struct S)+5*sizeof(int));
    struct S* p1=NULL;
	p->n=1;
    if(p==NULL)
    {
        return 0;
    }
    for(i=0;i<5;i++)
    {
        p->arr[i]=i;
    }
    p1=(struct S*)realloc(p,44);
    if(p1!=NULL)
    {
        p=p1;
        for(i=5;i<10;i++)
        {
            p->arr[i]=i;
        }
        for(i=0;i<10;i++)
        {
            printf("%d\n",p->arr[i]);
        }
    }
    free(p);
    p=NULL;
    return 0;
}

C语言内存分区

1.栈区(stack)

栈区是一种先进后出的内存结构,由编译器自动分配释放,存放函数的返回值、局部变量、函数参数、返回地址等。

2.堆区(heap)

用于动态内存分配。一般由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。

3.数据段(静态区)(static)

存放全局变量,静态数据,程序结束时由操作系统释放。

4.代码段

存放函数体的二进制代码。

上一篇:Linux磁盘空间查看命令


下一篇:FSAF:嵌入anchor-free分支来指导acnhor-based算法训练 | CVPR2019