Day 12 动态内存 3.28

为什么需要动态内存管理

因为当我们创建类型时,int类型固定是四个字节,char arr[10]固定是十个字节。但是我们对与空间的需要是在程序运行的时候不断发生变化的,所以这个时候数组的开辟空间就很可能不能满足。这个时候就需要动态内存开辟。

栈区:局部变量,函数的形参。

堆区:malloc/free,calloc,realloc,动态内存分配。

静态区:全局变量,静态变量。

动态内存函数

malloc和free

malloc用于开辟内存块,返回的是一个void*的指针,指向开辟空间的起始地址。当开辟空间不足而申请失败时返回一个NULL(空指针)。

#include 
//#include  两者都可以

void *malloc(size_t size);
int main()
{
	//int arr1[10];//40个字节
	//char arr2[40];//40个字节
	//申请空间
	int* ptr = (int*)malloc(40);    //空间过大,括号内为INT_MAX时会开辟失败。
	int* p = ptr;    //保存起始位置地址。

	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}
	//释放空间,指向起始位置的ptr。
	free(ptr);    //此时空间不属于ptr,但是ptr任然存放该空间首地址,所以为野指针。
	ptr = NULL;   //这样就让ptr没有任何有效空间。

	/*if (ptr != NULL)
	{
		*ptr = 100;
	}*/
	//*ptr = 100;//err
	return 0;
}
void free (void* ptr);

//free同样也包含在头文件中。
//ptr指向的空间需要为动态开辟的,不是的话那么就会报错(free函数未定义)。
//ptr如果是NULL指针,那么函数声明也没不干。

free要求:

当我们不释放动态申请的内存的时候,当程序结束,动态申请的内存有操作系统自动回收,如果程序不结束,就会造成内存泄漏。因为动态内存不会自动回收。

calloc

void *calloc(size_t num, size_t size);
//num是元素的个数,size是元素的长度。
//例如:
int* p =(int *)calloc(10,sizeof(int)); 
//calloc申请的空间会被初始化成0。

与malloc不同的是calloc不仅申请了空间,还把空间初始化成了0。

realloc

用于内存的调整。

void *realloc(void *memblock,size_t size);
//memblock是扩容空间的起始地址,size是扩充空间的大小,单位为字节。
//头文件同前三个函数。

 realloc函数会遇到两种情况,

成功:

当起始地址往后的内存足够的时候,便追加空间,追加空间和原本空间和为size。然后返回起始地址。

当起始地址往后内存不够的时候,会寻找一块长度足够的内存空间,把原数据拷贝过来,然后返回新内存空间的首地址。把原内存不够的数据的空间free掉。

失败:

返回一个空指针。因此需要ptr来接收。

注意的错误事项

int main()
{
	int*p = (int*)malloc(40);
	
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;//0 1 2 3 4 5 6 7 8 9
	}

	//空间不够,希望能放20个元素,考虑扩容
	int*ptr = (int*)realloc(p, 80);
	if (ptr != NULL)
	{
		p = ptr;
	}

	//扩容成功了,开始使用

	//不再使用,就释放
	free(p);
	p = NULL;

	return 0;
}

1.对NULL指针的解引用操作
解决办法:对malloc函数的返回值进行判断


int main()
{
	int* p = (int*)malloc(1000);
	int i = 0;
	if (p == NULL)
	{
		//....
		return 1;
	}
	//使用
	for (i = 0; i < 250; i++)
	{
		*(p + i) = i;
	}

	free(p);
	p = NULL;

	return 0;
}

2.对动态开辟空间的越界访问
对内存边界要检查


int main()
{
	int* p = (int*)malloc(100);
	int i = 0;
	if (p == NULL)
	{
		//....
		return 1;
	}
	//使用
	//越界访问了
	for (i = 0; i <= 25; i++)
	{
		*(p + i) = i;
	}

	return 0;
}

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

int main()
{
	int a = 10;

	int* p = &a;
	//.....

	free(p);
	p = NULL;
	return 0;
}
//因为a不是动态开辟的空间,所以会报错。

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

int main()
{
	int* p = (int*)malloc(100);
	if (p == NULL)
	{
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p = i;
		p++;    //不断++的时候释放时不再位于起始位置。
	}
	//释放空间
	free(p);//err
	p = NULL;

	return 0;
}

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

int main()
{
	int* p = malloc(100);
	if (p == NULL)
		return 1;
	
	free(p);
	//....
	free(p);//err

	p = NULL;

	return 0;
}

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

void test()
{
	int* p = malloc(100);
	//使用
}

int main()
{
	test();
	//.....
	while (1)
	{
		;
	}

	return 0;
}

你可能感兴趣的:(笔记,c语言)