文章目录
- 前言
- 一.动态内存
- 1.动态内存的用处
- 2.内存的布局
- 简单证明内存布局
- 栈向下生长的证明
- 堆向上增长的证明
- 3.malloc与free进一步理解
- 总结
前言
前提:
- 内存有基本的认识
- 内存函数基本的了解
如果你对内存与内存函数太不清楚可以看:动态内存管理
目标:
- 为什么要用动态内存?
- 内存的布局
- free只有一个参数,怎么将内存函数在堆上开辟的空间进行释放?
一.动态内存
1.动态内存的用处
int main()
{
char arr[1024*1024]={0};
return 0;
}
结果:
代码异常退出
当我们在栈上开辟1MB的大小的空间时超出了堆栈的大小。
注意:
堆栈==栈
堆栈!=堆
本文的堆是程序的空间,而在数据结构中,堆是一种完全二叉树的非线性结构!
结论:
- 栈的空间很小,大约为1MB。
- 堆的空间很大,大约为2GB
- 因此:动态内存,是为了满足更大数据的需求!
- 为什么要用动态内存?
2.内存的布局
- 这里的储存空间并不是内存!而是进程虚拟地址空间!
- 补充:具体涉及到操作系统的知识,只需了解即可!
简单证明内存布局
- 这里只证明除去代码区之外的部分。
#include<stdio.h>
#include <stdlib.h>
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;
}
- 方法:假设法证明
- 结论成立:从字符常量区的变量的地址到栈区的地址呈现增大趋势
结果:
结论:假设成立,结论成立!
栈向下生长的证明
同理
- 只需证明: 不断开辟变量,地址呈现减小的趋势
#include<stdio.h>
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;
}
结果:
结论:假设成立,结论成立!
堆向上增长的证明
同理
- 只需证明: 不断开辟变量,地址呈现增大的趋势
- 注意:这里开辟的空间要尽可能大。
#include<stdio.h>
#include<stdlib.h>
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;
}
结果:
结论:假设成立,结论成立!
3.malloc与free进一步理解
疑问产生:
- free只有一个参数怎么准确的释放目标空间?
- malloc只开辟了我们想要的空间吗?
为了解决这两个问题,那我们调试走起!
调试代码:
#include<stdio.h>
#include<stdlib.h>
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下调试
第一步:打开内存窗口
第二步:F9打断点
第三步:按F5走到断点处
第四步:在内存窗口输入arr加回车跳转到目标内存
假设:malloc只开辟这段空间,那么free只释放这段空间。
带着假设继续往下去:
第五步:按一下F10
很显然并不是只释放了这段空间。
因此:
- malloc并不是只开辟指定的空间大小
- free是通过malloc多开辟的空间来获取要释放空间的大小
图解:
我们可以理解: - free传入arr,向上访问可以知道malloc空间的准确大小,以及相关信息。
- 因此:free只传入一个参数即可释放开辟空间
疑问:那干脆数据都放在堆上算了,要栈有何用?
- 假设数据很小——1字节
- malloc开辟不止开辟1字节,还有相关信息(远大于1字节)
- 这样空间利用率极低,不如在栈上开辟!
- free只有一个参数,怎么将内存函数在堆上开辟的空间进行释放?
总结
文章不好,请不要点赞!