深入挖掘C语言 ----动态内存分配

发布于:2024-04-23 ⋅ 阅读:(24) ⋅ 点赞:(0)

开篇备忘录: "自给自足的光, 永远都不会暗"


正文开始

1. malloc和free

1.1 malloc

C语言提供了一个动态开辟内存的函数;

void* malloc (size_t size);

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

  • 如果内存开辟成功, 则返回一个指向开辟好空间的指针
  • 如果开辟失败, 则返回一个NULL指针, 因此malloc的返回值一定要做检查
  • 返回值的类型是void* ,所以malloc函数并不知道开辟空间的类型, 具体在使用的时候使用者自己来决定
  • 如果参数size为0, malloc的行为标准是未定义的, 取决于编译器

1.2 free

C语言提供了另外一个函数free, 专门是用来做动态内存释放和回收的, 函数原型如下:

void free (void* ptr);

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

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

malloc 和 free 都声明在stdlib.h 头文件中

int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(sizeof(int) * 10);
	if (ptr != NULL)
	{
		for (int i = 0; i < 10; i++)
		{
			*(ptr + 1) = 0;
		}
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

2. calloc和realloc

2.1 calloc

C语言还提供了一个函数叫做calloc, calloc函数也用来动态内存分配, 原型如下:

void* calloc (size_t num, size_t size);
  • 函数的功能是为num个大小为size的元素开辟一块空间, 并且把空间的每个字节都初始化为0
  • 与函数malloc的区别在于calloc会返回地址之前把申请的空间的每个字节初始化为0
int main()
{
	int* p = calloc(10, sizeof(int));
	if (p != NULL)
	{
		for (int i = 0; i < 10; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

运行程序:
在这里插入图片描述

所以如果我们对申请的内存空间的内容要求初始化, 那么可以很方便的使用calloch函数来完成任务.

2.2 realloc

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

函数原型如下:

void* realloc (void* ptr, size_t size);
  • ptr 是要调整的内存地址
  • size 调整之后新的大小
  • 返回值为调整之后的内存起始位置
  • 这个函数原内存空间大小的基础上, 还会将内存中的数据移动到新的空间
  • realloc 在调整内存空间的时候存在两种情况:
    1. 原有的空间之后有足够大的空间
    1. 原有的空间之后没有足够大的空间

两种情况如下图所示:

在这里插入图片描述

情况1:

要扩展内存就直接原有内存之后直接追加空间, 原来空间的数据不发生变化

情况2:

原有的空间之后没有足够多的空间, 扩展的方法是: 在堆空间上寻找另外一个1合适大小的连续空间来使用, 这样函数返回的就是一个新的内存的地址

由于上述两种情况, realloc函数的使用就要注意一些:

int main()
{
	int* ptr = (int*)malloc(100);
	if (ptr != NULL)
	{
		//业务处理
	}
	else 
	{
		return 1;
	}

	//扩充容量

	//代码1 - 直接将realloc的返回值放到ptr中
	ptr = (int*)realloc(ptr, 100);//(可以吗? , 如果开辟内存失败又会如何?)

	//代码2 - 先将realloc函数的返回值放在p中, 不为NULL,再放在ptr中

	int* p = NULL;
	p = realloc(ptr, 1000);
	if (p != NULL)
	{
		ptr = p;
	}
	//业务处理

	free(ptr);
	free(p);
	ptr = NULL;
	p = NULL;
	return 0;
}

3. 总结C/C++中程序内存区域划分

在这里插入图片描述

C/C++ 程序内存分配的几个区域:

1.栈区(stack): 在执行函数的时候, 函数内局部变量的存储单元都可以在栈上创建,
函数执行结束时这些存储单元自动被释放, 栈内存分配运算内置于处理器的指令集中, 
效率很高, 但是分配的内存容量有限, 栈区主要存放运行函数时而分配的局部变量, 
函数参数, 返回数据, 返回地址等. 这里推荐一本书<<函数栈帧的创建和销毁>>

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

3.数据段(静态区): (static) 存放全局变量, 静态数据, 程序结束后由操作系统释放.

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

本文内容到此结束, 还望读者点赞关注.