C语言之动态内存管理(动态内存函数)

  • 我们开辟空间的时候,空间的大小是固定的。
  • 在数组申明的函数,必须要指定数组的长度,它所需要的内存在编译时分配的。
  • 但是如果想要开辟不固定大小的空间,该怎么办?
  • 解决方式就是动态进行内存的分配,即在堆上开辟空间。
  • 本篇博客将介绍如何动态开辟空间。

一、动态内存函数

  • C语言中与动态内存管理相关的函数,主要有四个malloc、free、calloc和realloc。

1.malloc

  • void* malloc(size_t size);
  • 该函数是开辟一个大小为size字节大小的空间。
  • 返回值:若开辟失败,则返回一个NULL指针,因此malloc的返回值一定要作检查。若开辟成功,则返回这块空间的指针,因为该指针为void*类型,所以具体使用时可以进行强转。

2.free

  • void free(void* ptr);
  • 该函数是用来释放ptr指针指向的一块空间的。
  • 如果参数ptr指向的空间不是动态开辟的,则free函数的行为是未定义的。
  • 若果参数ptr是NULL指针,则该函数什么都不做。
  • 一般用了malloc申请空间,在使用之后一定要释放该空间,不然会造成资源泄露。
#include 
#include //malloc和free函数在该头文件里


int main()
{
	int num = 0;
	scanf("%d", &num);
	int* ptr = (int*)malloc(num * sizeof(int));
	if (NULL != ptr)
	{
		for (int i = 0; i < num; ++i)
		{
			*(ptr + i) = i;
			printf("%d ", *(ptr + i));
		}
		printf("\n");
	}

	free(ptr);
	ptr = NULL;//防止其成为野指针

	system("pause");
	return 0;
}

在这里插入图片描述

  • 最后一定要ptr设为NULL,不然会变成野指针。

3.calloc

  • void* calloc (size_t num, size_t size);
  • 该函数功能是为num个大小为size的元素开辟一块空间,并且开辟完之后该空间的每个字节都会初始化为0。
  • 其实该函数就是malloc加上初始化。
  • 返回值:若开辟失败,则返回一个NULL指针。若开辟成功,则返回这块空间的指针,因为该指针为void*类型,所以具体使用时可以进行强转。
  • 其开辟完空间,在这个空间使用完之后也一定要调用free函数将其释放掉。
#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include //calloc和free函数在该头文件里


int main()
{
	int num = 0;
	scanf("%d", &num);
	//int* ptr = (int*)malloc(num * sizeof(int));
	int* ptr = (int*)calloc(num, sizeof(int));
	if (NULL != ptr)
	{
		printf("使用之前,会自动初始化:\n");
		for (int i = 0; i < num; ++i)
		{
			printf("%d ", *(ptr + i));
		}
		printf("\n");
		printf("使用之后:\n");
		for (int i = 0; i < num; ++i)
		{
			*(ptr + i) = i;
			printf("%d ", *(ptr + i));
		}
		printf("\n");
	}

	free(ptr);
	ptr = NULL;

	system("pause");
	return 0;
}

C语言之动态内存管理(动态内存函数)_第1张图片

  • 不难发现,该函数作用和malloc一样,只不过比malloc多了一个初始化。

4.realloc

  • void* realloc (void* ptr, size_t size);
  • 该函数是当我们发现过去申请的空间太小了、或者太大了的时候,可以用该函数对动态开辟内存大小调整。
  • ptr是要调整的空间的指针。
  • size是调制之后的新大小。
  • 返回值为调整之后的内存起始位置。
  • 该函数在调整原内存空间大小的基础上,还会将原来内存中的数据移到新的空间中。
  • realloc在调整内存空间时会出现两种情况:①原有空间之后本身就有足够大的空间,此时只需要在原有内存之后直接追加空间,原来空间的数据不发生变化。 ②原有空间之后没有足够大的空间,此时要在堆上重新找一个适合大小的连续空间进行使用,此情况下函数返回的是一个新的内存地址。
#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include //ralloc和free函数在该头文件里


int main()
{
	int* ptr = (int*)malloc(5 * sizeof(int));
	if (NULL != ptr)
	{
		for (int i = 0; i < 5; ++i)
		{
			*(ptr + i) = i;
			printf("%d ", *(ptr + i));
		}
		printf("\n");
	}

	//用realloc扩容
	int* ptr2 = NULL;
	ptr2 = (int*)realloc(ptr, 6 * sizeof(int));
	if (NULL != ptr2)
	{
		ptr = ptr2;
	}
	//之前的数据都在
	*(ptr + 5) = 5;
	for (int i = 0; i < 6; ++i)
	{
		printf("%d ", *(ptr + i));
	}
	printf("\n");

	free(ptr);
	ptr = NULL;

	system("pause");
	return 0;
}

在这里插入图片描述

二、常见的动态内存错误

  • 对NULL指针的解引用
#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include 


int main()
{
	int* ptr = (int*)malloc(2 * sizeof(int));
	*ptr = 20;//不判断
	free(ptr);
	ptr = NULL;
	system("pause");
	return 0;
}

此时没有对ptr进行判断,则申请失败时ptr值为NULL,那么就会有问题。

  • 对动态开辟空间越界访问
#include 
#include //malloc和free函数在该头文件里


int main()
{
	int* ptr = (int*)malloc(5 * sizeof(int));
	if (NULL != ptr)
	{
		for (int i = 0; i < 6; ++i)
		{
			*(ptr + i) = i;
			printf("%d ", *(ptr + i));
		}
		printf("\n");
	}
	free(ptr);
	ptr = NULL;
	system("pause");
	return 0;
}

C语言之动态内存管理(动态内存函数)_第2张图片
上面 *(ptr + 5) 就是越界访问了。

  • 对不是动态开辟的内存使用了free函数进行释放
#include 
#include 


int main()
{
	int i = 20;
	int* ptr = &ii;
	free(ptr);//
    ptr = NULL;
	system("pause");
	return 0;
}

C语言之动态内存管理(动态内存函数)_第3张图片
此时程序会崩。

  • 对同一块动态内存多次释放
#include 
#include 


int main()
{
	int* ptr = (int*)malloc(5 * sizeof(int));
	if (NULL != ptr)
	{
		for (int i = 0; i < 6; ++i)
		{
			*(ptr + i) = i;
			printf("%d ", *(ptr + i));
		}
		printf("\n");
	}
	free(ptr);
	free(ptr);
	ptr = NULL;
	system("pause");
	return 0;
}

C语言之动态内存管理(动态内存函数)_第4张图片
此时程序会崩。

  • 使用free函数释放一块动态开辟内存的一部分
#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include //malloc和free函数在该头文件里


int main()
{
	int* ptr = (int*)malloc(5 * sizeof(int));
	
	++ptr;//ptr不再指向该动态内存的起始位置了。
	free(ptr);
	ptr = NULL;
	system("pause");
	return 0;
}

C语言之动态内存管理(动态内存函数)_第5张图片
程序依旧会崩。

  • 当我们动态开辟内存空间时,使用完之后忘记使用free函数释放之后就会造成内存泄漏。

三、C语言内存开辟

  • C语言中程序的内存区域划分
    C语言之动态内存管理(动态内存函数)_第6张图片

  • 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

  • 堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于链表。

  • 数据段(静态区)(static):存放全局变量、静态数据。程序结束后由系统释放。

  • 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

你可能感兴趣的:(C语言)