动态内存管理

一、动态内存函数

malloc&free

void *malloc( size_t size );

函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  • 需要头文件<stdlib.h>,size表示需要开辟内存空间的大小(字节)
  • 开辟成功则返回指向这个空间的指针,类型为void*,需要转换为相应类型
int main()
{
	int* i=(int*)malloc(40);
	if (i == NULL)
	{
		printf("%s\n", strerror(errno));
		return;
	}
	return 0;
}
  • 开辟失败则返回空指针NULL,因此在使用malloc的返回值之前需要先检查

void free( void *memblock );

free函数用来释放动态开辟的内存。

  • 如果参数指向的空间不是动态开辟的,则会 报错
  • 如果参数为NULL,则什么都不做
free(i);
i = NULL;

calloc

功能与malloc相似,但是calloc的参数不同,同时calloc会将其开辟的空间初始化为0

void *calloc( size_t num, size_t size );

  • 功能:为num个大小为size的元素开辟空间,并将空间的每个字节初始化为0

​​​​​​realloc

void *realloc( void *memblock, size_t size );
  • 功能:将memblock所指向的内存地址调整成为size个字节的空间,并返回新空间的内存起始位置
  • 开辟新空间的几种情况:

        1.当原有空间之后有足够大的连续空间,则原有数据不变,直接在原有空间后追加空间

        2.当原有空间之后没有足够大的连续空间,则会在堆区寻找另一个合适大小的连续空间使用,并将原有空间的数据拷贝到新空间,再释放原有空间,最后返回新空间的地址

        3.在开辟新空间时,同样可能开辟失败,此时会返回NULL,在使用之前需要先检查

int main()
{
	int* i=(int*)malloc(40);
	int*ptr = (int*)realloc(i, 80);//创建临时指针接收返回值
	if (ptr == NULL)//判断是否返回NULL
	{
		i = ptr;//不为NULL则将返回值给i
	}	
	return 0;
}

常见动态内存错误

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

int main()
{
	int* i = (int*)malloc(40);
	*i = 9;//如果malloc返回的是空指针,则会报错
}

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

int main()
{
	int* p = (int*)malloc(40);
	if(p==NULL)
        return 1;
    else
    {
       for(int j=0;j<=10;j++)
        {
            *(p+j)=j;
            //当j=10时,会造成越界访问
        }         
    }
}

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

int main()
{
    int i=10;
    free(&i);//free只能释放动态开辟的内存
    return 0;
}

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

int main()
{
	int* p = (int*)malloc(40);
	if(p==NULL)
        return 1;
    else
    {
       for(int j=0;j<=5;j++)
        {
            *p=j;
            p++;
        }         
    }
    free(p);//只释放了动态开辟的部分内存空间 ××
    return 0;
}

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

int main()
{
	int* i = (int*)malloc(40);
	//...
    free(p);
    //在释放空间后,可加上 p=NULL; 多次释放也不会报错
    //...
    free(p);//多次对p进行释放×××
}

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

void emo()
{
    int* i=(int*)malloc(40);
    //忘记释放动态内存
}
int main()
{
    emo();
    return 0;
}

二、柔性数组

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

例如:        

typedef struct stu
{
	int i;
	int arr[0];//柔性数组成员
    //或者写成 int arr[];
}Stu;

2.1柔性数组特点

  • 要求:在柔性数组成员前必须至少要一个其他成员
  • sizeof返回的这种结构不会包括柔性数组的内存
  • 内存分配:使用动态内存函数malloc等进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小

2.2柔性数组的使用

int main()
{
	Stu* s1 = (Stu*)malloc(sizeof(struct stu) + 40);
	//开辟空间的大小等于 结构体其他成员所占内存+柔性数组所要求内存
	if (s1 == NULL)
		return 1;
	s1->i = 100;
	for (int j = 0; j < 10; j++)
	{
		s1->arr[j] = j;
	}
	for (int j = 0; j < 10; j++)
	{
		printf("%d ", s1->arr[j]);
	}
	//扩容
	Stu* ptr = (Stu*)realloc(s1, sizeof(Stu) + 80);
	if (ptr == NULL)
		return 1;
	s1 = ptr;
	free(s1);
	s1 = NULL;

}

2.3柔性数组的优势

typedef struct stu
{
	int i;
	int* arr;
}Stu;
int main()
{
	//将结构体放在堆区
	Stu* s1 = (Stu*)malloc(sizeof(struct stu));
	//判断是否为NULL省去
	s1->i = 100;
	s1->arr = (int*)malloc(40);
	for (int j = 0; j < 10; j++)
	{
		s1->arr[j] = j;
	}
	for (int j = 0; j < 10; j++)
	{
		printf("%d ", s1->arr[j]);
	}
	//扩容
	Stu* ptr = (Stu*)realloc(s1->arr, 80);
	//判断NULL省去
	s1->arr = ptr;
	//释放
	free(s1->arr);
	free(s1);//这里在释放s1后就清空了s1所指向的空间的成员
	//包括arr,因此不用将arr赋成NULL
	s1 = NULL;
}
  • 相比于指针,柔性数组只需要一次就能把所有内存释放掉,方便内存释放
  • 连续的内存有益于提高访问速度,也有益于减少内存碎片
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值