动态内存开辟

(一)三种方式

  内存中存在三种空间,分别是栈区(用来存放局部变量和函数形式的参数),堆区(动态内存分配),静态区(存放全局变量和静态变量)

(二)动态内存函数

1.malloc和free

  malloc是申请开辟动态内存分配空间函数,头文件为<stdlib.h> 和<malloc.h>,在MSDN上解释为void *malloc( size_t size );  如果开辟成功,那么返回值为指向这块空间的指针,如果开辟失败,那么返回NULL。
  free是释放内存空间函数,和malloc一起使用。

#include <stdio.h>
#include <malloc.h>
int main()
{
	int* p = (int*)malloc(40);
	//开辟内存空间
	if (p == NULL)//开辟失败
	{
		return -1;
	}
	for (int i = 0;i < 10;i++)
	{
		*(p + i) = i;
	}
	free(p);
	//释放内存空间
	return 0;
}

  在内存中显示如下,

2.calloc

  calloc也是开辟内存空间函数,MSDN上解释为void *calloc( size_t num, size_t size );,与malloc函数不同,calloc函数在堆区开辟空间后初始化为0,其中,num为元素的个数,size为每个元素的字节长度。

#include <stdio.h>
#include <string.h>
int main()
{
	int* p = (int *)calloc(10,sizeof(int));
	//开辟内存空间
	if (p == NULL)//开辟失败
	{
		printf("%d", strerror(errno));
		return -1;
	}
	for (int i = 0;i < 10;i++)
	{
		printf("%d ", *(p + i));
		//输出为0 0 0 0 0 0 0 0 0 0
	}
	free(p);
	//释放内存空间
	return 0;
}

3.realloc

  realloc也是内存开辟函数,要在原有开辟的基础上再次开辟,相当于增加空间,在MSDN上解释为void *realloc( void *memblock, size_t size );,头文件为<stdlib.h>或者<malloc.h>,其中memblock是原来开辟内存空间的起始地址,size是再次开辟的内存空间之后的总大小,单位是字节。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
	int* p = (int *)calloc(10,sizeof(int));
	//开辟内存空间
	if (p == NULL)//开辟失败
	{
		printf("%d", strerror(errno));
		return -1;
	}
	int* ch = (int*)realloc(p, 20 * sizeof(int));
	if (ch != NULL)
	{
		p = ch;//开辟成功
	}
	else
	{
		return -1;//开辟失败
	}
	for (int i = 10;i < 20;i++)
	{
		*(p + i) = i;
	}
	for (int j = 0;j < 20;j++)
	{
		printf("%d ", *(p + j) );
	}
	free(p);
	//释放内存空间
	return 0;
}

  realloc函数在内存中开辟空间的形式如下,如果后续空间足够大,那么就直接开辟;如果后续空间不足,那么将向后寻找直至找到足够开辟空间的位置,如果找不到,那么开辟失败,返回NULL。如图所示,

(三)常见错误

1.对空指针进行解引用

#include <stdio.h>
int main()
{
	int* p = (int*)malloc(20);
	*p = 20;//error
	return 0;
}

  如果开辟内存空间失败,那么p为空指针,无法进行操作。应该加判断,

	if (p == NULL)
	{
		return -1;
	}
	else
	{
		*p = 20;
	}

2.对动态内存开辟的越界访问

#include <stdio.h>
int main()
{
	int* p = (int*)malloc(5*sizeof(int));
	if (p == NULL)
	{
		return -1;
	}
	for (int i = 0;i < 10;i++)//error
	{
		*(p + i) = i;//赋值
	}
	for (int j = 0;j < 10;j++)
	{
		printf("%d ", *(p + j));//打印
	}
	free(p);
	return 0;
}

  在这里malloc函数开辟的空间为五个整型的大小,在赋值时,却对十个整型进行赋值,导致动态内存开辟的越界访问,运行显示错误。

3.与free有关的错误

free释放非动态开辟内存

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	free(p);//error
	p = NULL;
	return 0;
}

  这里的p开辟的地址被放在栈区,而free是对堆区的内存进行操作,运行报错。

free释放动态内存空间的一部分

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

  free指向的应该是释放空间的头地址,p++过后,p已经不再指向起始地址了,运行产生错误。

free对同一块空间多次释放

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

  两次释放p的空间,error,可以在每次释放之后给p赋值为NULL以避免这种错误。

动态开辟内存忘记释放/内存泄漏

  动态开辟内存或通过free手动释放或者在程序结束时进行自我回收。如果不进行free释放空间,程序一直运行且不结束的话,则会存在内存泄漏,除非程序重启。

(四)柔性数组

  结构体中允许最后一个成员是未知大小的数组,这就叫做柔性数组。如下例所示,有两种表示方式,(注意数组之前一定至少有一个其他成员)

struct st_type1
{
	int x;
	int a[];
};
//或者
struct st_type1
{
	int x;
	int a[0];
};

  当用sizeof计算含有柔性数组成员的时候,其结果不考虑柔性数组成员。包含柔性数组成员的结构用malloc函数进行动态内存分配,且分配的内存应该大于数组的大小以适应柔性数组成员

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
struct st_type1
{
	int x;
	int a[0];
};
int main()
{
	struct st_type1*p = (struct st_type1*)malloc(sizeof(struct st_type1) + 10 * sizeof(int));
	//给柔性数组成员开辟足够的空间
	if (p == NULL)
	{
		printf("%S\n", strerror(errno));
		return -1;
	}
	p->x = 10;
	for (int i = 0;i < 10;i++)//赋值
	{
		p->a[i] = i;
	}
	for (int j = 0;j < 10;j++)//打印
	{
		printf("%d ", p->a[j]);
	}
	struct st_type1* pr = (struct st_type1*)realloc(p, sizeof(struct st_type1) + 20 * sizeof(int));
	//如果空间不够,再次开辟
	if (pr == NULL)//开辟失败
	{
		printf("%S\n", strerror(errno));
		return -1;
	}
	else
	{
		p = pr;
	}
	free(p);
	return 0;
}

  这样柔性数组和结构体结合,动态内存开辟以改变结构体的大小,如果不使用柔性数组的话,代码如下,

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
struct st_type1
{
	int x;
	int *a;
};
int main()
{
	struct st_type1*p = (struct st_type1*)malloc(sizeof(struct st_type1));
	if (p == NULL)
	{
		printf("%S\n", strerror(errno));
		return -1;
	}
	p->x = 10;
	p->a = (int *)malloc(10*sizeof(int));//开辟a指向的空间
	for (int i = 0;i < 10;i++)//赋值
	{
		p->a[i] = i;
	}
	for (int j = 0;j < 10;j++)//打印
	{
		printf("%d ", p->a[j]);
	}
	int* pr = (int *)realloc(p->a, 10 * sizeof(int));
	if (pr == NULL)
	{
		printf("%S\n", strerror(errno));
		return -1;
	}
	else
	{
		p->a = pr;
	}
	free(p->a);//释放a指向的空间
	free(p);//释放结构体的空间
	return 0;
}
  相对于不使用柔性数组,使用柔性数组动态开辟的内存是连续的,运行速度相对较快。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值