【深度解刨C语言】内存管理(详)


文章目录

  • 前言
  • 一.动态内存
    • 1.动态内存的用处
    • 2.内存的布局
        • 简单证明内存布局
        • 栈向下生长的证明
        • 堆向上增长的证明
    • 3.malloc与free进一步理解
  • 总结

前言

前提:

  • 内存有基本的认识
  • 内存函数基本的了解

如果你对内存与内存函数太不清楚可以看:动态内存管理

目标:

  • 为什么要用动态内存?
  • 内存的布局
  • free只有一个参数,怎么将内存函数在堆上开辟的空间进行释放?

一.动态内存

1.动态内存的用处

int main()
{
	char arr[1024*1024]={0};
	return 0;
}

结果:
代码异常退出
【深度解刨C语言】内存管理(详)_第1张图片
当我们在栈上开辟1MB的大小的空间时超出了堆栈的大小

注意:
堆栈==栈
堆栈!=堆
本文的堆是程序的空间,而在数据结构中,堆是一种完全二叉树的非线性结构!

结论:

  • 栈的空间很小,大约为1MB。
  • 堆的空间很大,大约为2GB
  • 因此:动态内存,是为了满足更大数据的需求!
  • 为什么要用动态内存?

2.内存的布局

【深度解刨C语言】内存管理(详)_第2张图片

  • 这里的储存空间并不是内存!而是进程虚拟地址空间
  • 补充:具体涉及到操作系统的知识,只需了解即可!

简单证明内存布局

  • 这里只证明除去代码区之外的部分
#include
#include 
int g_val;//初始化的全局变量
int g_val_1 = 0;//未初始化的全局变量
int main()
{

	int a = 0;//栈区的变量
	int* arr = (int*)malloc(sizeof(int) * 20);//将堆区的地址赋值给arr
	char* str = "abcdef";//将字符常量区的地址赋值跟str
	
	printf("字符常量区:            %p\n", str);
	printf("初始化的全局变的内存:  %p\n", &g_val_1);
	printf("未初始化全局变量的内存:%p\n", &g_val);
	printf("堆区:                  %p\n", arr);
	printf("栈区:                  %p\n", &a);

	free(arr);
	arr = NULL;
	return 0;
}
  1. 方法:假设法证明
  2. 结论成立:从字符常量区的变量的地址到栈区的地址呈现增大趋势

结果:
【深度解刨C语言】内存管理(详)_第3张图片
结论:假设成立,结论成立!

栈向下生长的证明

同理

  • 只需证明: 不断开辟变量,地址呈现减小的趋势
#include

int main()
{
	
	int a = 0;
	int b = 0;
	int c = 0;
	int d = 0;
	printf("a:%p\n", &a);
	printf("b:%p\n", &b);
	printf("c:%p\n", &c);
	printf("d:%p\n", &d);

	return 0;
}

结果:
【深度解刨C语言】内存管理(详)_第4张图片
结论:假设成立,结论成立!

堆向上增长的证明

同理

  • 只需证明: 不断开辟变量,地址呈现增大的趋势
  • 注意:这里开辟的空间要尽可能大
#include
#include
int main()
{
	int* a = (int*)malloc(sizeof(int) * 1024);
	int* b = (int*)malloc(sizeof(int) * 1024);
	int* c = (int*)malloc(sizeof(int) * 1024);
	int* d = (int*)malloc(sizeof(int) * 1024);

	printf("%p\n", a);
	printf("%p\n", b);
	printf("%p\n", c);
	printf("%p\n", d);

	free(a);
	free(b);
	free(c);
	free(d);

	a = NULL;
	b = NULL;
	c = NULL;
	d = NULL;

	return 0;
}

结果:
【深度解刨C语言】内存管理(详)_第5张图片
结论:假设成立,结论成立!

3.malloc与free进一步理解

疑问产生:

  • free只有一个参数怎么准确的释放目标空间
  • malloc只开辟了我们想要的空间吗?

为了解决这两个问题,那我们调试走起!

调试代码:

#include
#include
int main()
{

	char* arr = (char*)malloc(sizeof(char) * 10);
	if (arr == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
	}
	free(arr);
	arr = NULL;
	return 0;
}

VS2019下调试
第一步:打开内存窗口
【深度解刨C语言】内存管理(详)_第6张图片
第二步:F9打断点
【深度解刨C语言】内存管理(详)_第7张图片
第三步:按F5走到断点处
第四步:在内存窗口输入arr加回车跳转到目标内存
【深度解刨C语言】内存管理(详)_第8张图片

假设:malloc只开辟这段空间,那么free只释放这段空间。
带着假设继续往下去:
第五步:按一下F10
【深度解刨C语言】内存管理(详)_第9张图片

很显然并不是只释放了这段空间。
因此:

  • malloc并不是只开辟指定的空间大小
  • free是通过malloc多开辟的空间来获取要释放空间的大小
    图解:
    【深度解刨C语言】内存管理(详)_第10张图片
    我们可以理解:
  • free传入arr,向上访问可以知道malloc空间的准确大小,以及相关信息。
  • 因此:free只传入一个参数即可释放开辟空间

疑问:那干脆数据都放在堆上算了,要栈有何用?

  • 假设数据很小——1字节
  • malloc开辟不止开辟1字节,还有相关信息(远大于1字节)
  • 这样空间利用率极低,不如在栈上开辟!
  • free只有一个参数,怎么将内存函数在堆上开辟的空间进行释放?

总结

文章不好,请不要点赞!

你可能感兴趣的:(C语言深度解刨,c语言)