内存管理
堆,栈,静态区
c程序地址空间分布规则:
栈是从高地址向低地址延伸的,后创建的变量,后入栈,那么地址就越小.
静态变量,作用域不变,声明周期发生改变.本质原因是存储位置发生改变.编译器编译的时候放到了全局数据区.
#include<stdio.h>
#include<stdlib.h>
int g_val2;
int g_val1=10;
int main()
{
int x=10;
static int y=10;
printf("代码区:%p\n",main);
const char* str="hello yuanwei";
printf("字符常量区:%p\n",str);
printf("已初始化常量区:%p\n",&g_val1);
printf("未初始化常量区:%p\n",&g_val2);
char *p1=(char*)malloc(sizeof(char)*10);
char *p2=(char*)malloc(sizeof(char)*10);
char *p3=(char*)malloc(sizeof(char)*10);
printf("堆区:%p\n",p1);
printf("堆区:%p\n",p2);
printf("堆区:%p\n",p3);
printf("栈区:%p\n",&str);
printf("栈区:%p\n",&p1);
printf("x :%p\n",&x);
printf("y:%p\n",&y);
free(p1);
free(p2);
free(p3);
}
动态内存
动态内存开辟
#define N 10
int main()
{
int* p = (int*)malloc(sizeof(int) * N);
if (NULL == p)
{
perror("malloc fail\n");
exit(1);
}
for (int i = 0; i < N; i++)
{
*(p + i) = i;
}
free(p);//堆空间申请的内存不要忘记释放
return 0;
}
为什么要有动态内存
栈空间有限,申请不了大量内存空间.在技术方面,普通的空间申请,都是在全局或者栈区,全局一般不太建议大量使用,而栈空间有限,那么如果一个应用需要大量的内存空间的时候,需要通过申请堆空间来支持基本业务 .
在应用方面,程序员很难一次预估好自己总共需要花费多大的空间。想想之前我们定义的所有数组,因为其语法约束,我们必须得明确"指出"空间大小.但是如果用动态内存申请(malloc),因为malloc是函数,而函数就可以传参,也就意味着,我们可以通过具体的情况,对需要的内存大小进行动态计算,进而在传参申请,提供了很大的灵活性.
指针合法性
指针合法:能够被用户直接使用的,应用层面解决的.指针如果有具体指向(包含野指针),对应合法性我们是无法验证的,确认指针具体值得合法性,不是用户能做到的.
- 所有的指针,如果没有被直接使用,必须设置为NULL(编程规范)
- 函数内部,要验证指针的合法性,本质是验证指针!=NULL
系统提供检测指针是否合法的宏assert();
如果内部条件不满足则中断运行,调试代码时用.assert()
也无法检查野指针(随机值),语言层面是无法解决系统层面的问题的.
将开辟的空间初始化不是必须的,但是推荐.memset()
内存越界
在遍历内存空间的时候越界一些不一定会报错.并不是所有的严重问题都会显示出来.
内存泄漏
申请内存不释放.
-
如果程序退出了,内存泄漏还存在吗?不在
-
什么样的程序最怕内存泄漏?永远不会退出的程序(操作系统,杀毒软件,服务器程序(24小时在线))
free
只知道堆空间的起始地址,并没有告诉他要释放多少字节.
释放的字节数比10字节要多,说明最开始申请的字节就比10字节多.
实际malloc申请空间时,系统给你的其实更多.多出来的部分就是记录这次申请的更详细的信息(内存级cookie信息)
-
所以申请堆空间,是申请大空间更好,降低了cookie所占的比率.
-
释放之后堆空间指针并不会被设置为NULL.
free理解
经发现,堆空间释放前后,栈中的指针变量的值并没有被设置为NULL.释放后的空间也不能再被使用.
- 那么所谓的释放究竟是什么?
free是在取消地址p和空间的关系,p里面的地址值可以相同但是不可再访问空间.关系也是需要用数据去维护的.
C中动态内存“管理”体现在哪
内存管理的本质其实是:空间什么时候申请,申请多少,什么时候释放,释放多少的问题。
- 场景:C的内存管理工作是由程序员决定的,而程序员什么时候申请,申请多少,什么时候释放,释放多少都是有场景决定的(比如上面的链表操作),而大部分书中,是讲具体操作,很少有场景,所以管理工作体现的并不直观。不过我们现在能理解即可。
- 其他高级语言:像java这样的高级语言,语言本身自带了内存管理,所以程序员只管使用即可。换句话说,内存管理工作,程序员是不用关心的。但是C是较为底层的语言,它的内存管理工作是暴露给程序员的,从而给程序员提供了更多的灵活性,不过,管理工作也同时交给了程序员.
链表测试:
#define N 10
typedef struct Node
{
int data;
struct Node* next;
}Node_t;
Node_t* AllocNode(int data)
{
Node_t* node = (Node_t*)malloc(sizeof(Node_t));
if (node == NULL)
{
perror("malloc failed\n");
exit(1);
}
node->data = data;
node->next = NULL;
return node;
}
void InsertNode(Node_t* head, int x)
{
assert(head);
Node_t* node = AllocNode(x);
node->next = head->next;
head->next = node;
}
void ShowList(Node_t* cur)
{
assert(cur);
cur = cur->next;
while (cur)
{
printf("%d ",cur->data);
cur = cur->next;
}
printf("->NULL\n");
}
void DeleteNode(Node_t* head)
{
assert(head);
Node* p = head->next;
head->next = p->next;
free(p);
p = NULL;
}
int main()
{
Node_t* head = AllocNode(0);
printf("插入测试开始:...\n");
for (int i = 1; i <= N; i++)
{
InsertNode(head,i);
ShowList(head);
Sleep(1000);
}
printf("删除测试开始:...\n");
for (int i = 1; i <= N; i++)
{
DeleteNode(head);
ShowList(head);
Sleep(1000);
}
system("pause");
return 0;
}