动态内存管理

本文介绍了C/C++中的动态内存管理函数malloc,free,calloc,realloc,以及它们的作用和常见内存错误,如越界访问、多次释放、内存泄漏等。同时,还分析了栈、堆内存的区别和柔性数组的使用方法及注意事项。
摘要由CSDN通过智能技术生成

动态内存管理

malloc函数

void* malloc (size_t size);

  • 参数为要申请空间的大小,返回值为void *,具体使用在于操作者
  • 如果申请成功会返回空间的起始位置,
  • 如果申请失败会返回空指针null

free函数

void free (void* ptr);

free函数是专门用来做动态内存释放和回收的

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

calloc函数

void* calloc (size_t num, size_t size);

  • 函数功能是开辟num个大小为size的空间,并且把每个空间的字节初始化成0
  • 与函数malloc的区别是,calloc在返回指针前会把清空目标内存存储的值

realloc函数

  • realloc让动态内存管理更加灵活
  • 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
    候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小
    的调整

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

  • ptr为要调整的指针
  • size为重新申请空间的大小
  • 返回值为调整后内存的起始位置
  • 这个函数在在调整的时候会出现两种情况
  • 1.原内存空间后内存足够大,直接追加开辟
  • 2.原内存空间后内存不够大,将原内存的数据迁移到新开辟的一块空间中,然后将原来的内存free,返回新内存的空间

在这里插入图片描述

常见的内存错误

1.对NULL的解引用

int main(){
    char *p=(char *)malloc(__INT_MAX__*4);
    *p = 1;
    printf("%d",p);
}

malloc函数如果申请内存失败会返回Null,所以在使用动态申请的内存之前,一定要进行判断。

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

int main() {
	 int *p = (int *)calloc(10,4);
	 for (int i = 0; i < 11; i++)
	 {
		 *(p + i) = i;
	 }
	 free(p);
	/*test();*/
}

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

int  main(){
    int arr[]={1,2,3,4,5};
    free(arr);
}

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

int main(){
    char *p = malloc(5);
    free(p);
    free(p);
}

free函数不会改变p指针存储的地址,只会释放p指针指向内存存储的值,所以为了避免多次释放,在free后要将指针置为Null,free(Null)不会出现错误。

5.内存泄漏

void mmalloc(){
    char *p = malloc(1);
}
int main(){
    mmalloc();
}

在调用方法的时候动态申请了内存,方法结束没有释放那块内存,那就永远找不到那块内存了,那块内存的空间也无法释放,所以在函数里申请内存,函数的返回值必须为申请内存的地址。

几个经典的笔试题

题目1

void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}

p是形式参数,p的值是str的临时拷贝,所以给p开辟空间只是给形参开辟了一块空间,当函数结束,形参就会被销毁,这块空间就无法释放,也无法给str赋值。

题目2

char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}

函数中的变量都在堆空间,函数结束时自动销毁。在函数内创建了数组p,随后将p的地址返回。但是函数结束时,变量p的空间就会被回收,所以p指向的是未知的。

题目3

void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}

最后没有free内存和将指针置为null

题目4

void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}

即使free掉空间,也是改变指针指向那块区域里的值,而不是改变指针指向。

C/C++程序内存的开辟

在这里插入图片描述

  • 函数内的变量,形参都在栈开辟
  • 用过malloc等手动申请的内存都在堆空间
  • 全局数据和静态数据存储在数据段

柔性数组

C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员

例如

typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;

特点

  • 结构中的柔性数组成员前面必须至少一个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存。
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大
    小,以适应柔性数组的预期大小
//code1
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4
int main(){
    type_a *p = (type_a *)malloc(sizeof(type_a)+5*sizeof(int));
    p->i = 3;
    for (int i = 0; i < 5; i++)
    {
        p->a[i] = i;
    }
    for (int i = 0; i < 5; i++)
    {
       printf("%d ",p->a[i]);
    }
    free(p);
    p=null;
}

开辟的空间除了结构体就是柔性数组。

可以使用指针模拟柔性数组

typedef struct st_type2
{
int i;
int *a;//使用指针模拟数组
}type_b;
int main(){
    type_b *p = malloc(sizeof(type_b));
    p->i = 100;
    p->a = malloc(5*sizeof(int));
        for (int i = 0; i < 5; i++)
    {
        p->a[i] = i;
    }
    for (int i = 0; i < 5; i++)
    {
       printf("%d ",p->a[i]);
    }
    free(p->a);
    p->a = NULL;
    free(p);
    p = NULL;
    return 0;
}

与柔性数组的区别

  • 结构体的大小还包括指针的大小,而柔性数组的结构体是不包含柔性数组的大小的。
  • 指针申请的空间需要手动释放,且与结构体所在的空间不连续。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值