C-内存管理

内存管理

堆,栈,静态区

c程序地址空间分布规则:

栈是从高地址向低地址延伸的,后创建的变量,后入栈,那么地址就越小.

image-20230512123023817

静态变量,作用域不变,声明周期发生改变.本质原因是存储位置发生改变.编译器编译的时候放到了全局数据区.

image-20230512122920529

#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是函数,而函数就可以传参,也就意味着,我们可以通过具体的情况,对需要的内存大小进行动态计算,进而在传参申请,提供了很大的灵活性.

指针合法性

指针合法:能够被用户直接使用的,应用层面解决的.指针如果有具体指向(包含野指针),对应合法性我们是无法验证的,确认指针具体值得合法性,不是用户能做到的.

  1. 所有的指针,如果没有被直接使用,必须设置为NULL(编程规范)
  2. 函数内部,要验证指针的合法性,本质是验证指针!=NULL

系统提供检测指针是否合法的宏assert();如果内部条件不满足则中断运行,调试代码时用.assert()也无法检查野指针(随机值),语言层面是无法解决系统层面的问题的.

将开辟的空间初始化不是必须的,但是推荐.memset()

内存越界

在遍历内存空间的时候越界一些不一定会报错.并不是所有的严重问题都会显示出来.

内存泄漏

申请内存不释放.

  • 如果程序退出了,内存泄漏还存在吗?不在

  • 什么样的程序最怕内存泄漏?永远不会退出的程序(操作系统,杀毒软件,服务器程序(24小时在线))

free

只知道堆空间的起始地址,并没有告诉他要释放多少字节.

释放的字节数比10字节要多,说明最开始申请的字节就比10字节多.

image-20230513212842321

实际malloc申请空间时,系统给你的其实更多.多出来的部分就是记录这次申请的更详细的信息(内存级cookie信息)

  • 所以申请堆空间,是申请大空间更好,降低了cookie所占的比率.

  • 释放之后堆空间指针并不会被设置为NULL.

free理解

image-20230514092119567

经发现,堆空间释放前后,栈中的指针变量的值并没有被设置为NULL.释放后的空间也不能再被使用.

  • 那么所谓的释放究竟是什么?

free是在取消地址p和空间的关系,p里面的地址值可以相同但是不可再访问空间.关系也是需要用数据去维护的.

C中动态内存“管理”体现在哪

内存管理的本质其实是:空间什么时候申请,申请多少,什么时候释放,释放多少的问题。

  1. 场景:C的内存管理工作是由程序员决定的,而程序员什么时候申请,申请多少,什么时候释放,释放多少都是有场景决定的(比如上面的链表操作),而大部分书中,是讲具体操作,很少有场景,所以管理工作体现的并不直观。不过我们现在能理解即可。
  2. 其他高级语言:像java这样的高级语言,语言本身自带了内存管理,所以程序员只管使用即可。换句话说,内存管理工作,程序员是不用关心的。但是C是较为底层的语言,它的内存管理工作是暴露给程序员的,从而给程序员提供了更多的灵活性,不过,管理工作也同时交给了程序员.

链表测试:

image-20230514095718125

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值