c语言内存管理刨析

153 阅读3分钟

内存管理

  • c程序动态地址空间分布

  • 无论什么变量,都要和这张图相对应 1122.png

  • static修饰后,它的生命周期变成全局的了?

    • 编译的时候编进了全局数据区
  • 验证指针合法性

    • 指针如果有具体的指向(包括野指针),对应的合法性我们无法验证,确认指针的正常值的合法性,不是用户能做到的。
    • 所有的指针,如果没有被直接使用,必须设置为NULL
    • 在函数内部要验证指针的合法性,本质是验证指针!=NULL
    • 在内部有一个宏:assert 检测入口指针是否合法,在调试的时候,不是很推荐
    • “合法”:能够被用户直接使用!应用层面解决
struct str
{
	char* name;
	int scope;
};

void showprint(char *str)
{
	// 检测入口指针是否合法,在调试的时候
	assert(str);
	// if (str == NULL)return;
	printf("%s", str);
	// 合法性
}

int main()
{
	struct str zhan = { NULL,0 };
	zhan.name = malloc(32);
	strcpy(zhan.name, "tom");

	showprint(zhan.name);
	
	return 0;
}

  • 在开辟空间后,最好是初始化一下
  • 空间越界,不一定报错,有可能还是能访问的
    • 越界是一个很严重的问题,但是不一定能表现出来
int main()
{
	// 开辟空间
	int* p = malloc(sizeof(int) * 30);
	// 初始化空间
	memset(p, 0, sizeof(int) * 30);

	for (int i = 0; i < 30; i++)
	{
		printf("%d \n", p[i]);
	}

	return 0;
}

  • 内存泄漏,只申请不释放
  • 如果程序退出了内存泄漏问题还存在吗?
    • 内存泄漏:什么样的程序,最怕内存泄漏问题?
      • 永远不会主动退出的程序最害怕。
      • 操作系统本身,杀毒软件,服务器程序
      • 常驻(内存)进程(程序)

int main()
{
	while (1)
	{
		int* p = malloc(1024);
	}

	return 0;
}

free

  • 实际malloc申请空间的时候,系统给你的其实更多!
  • 多出来的部分:
    • 记录这次申请的更详细信息
    • 申请了多大的空间
    • 申请堆空间,大空间好,还是小空间好?
    • 大空间好。
    • 栈上申请小空间。
int main()
{
	// 释放的字节实际上要比申请的多的多
	// 意味着申请一定不止 20 个字节
	char* p = (char*)malloc(sizeof(char) * 20);

	// 我们目前只知道堆空间的起始地址
	// 并没有要传入释放多少字节
	free(p);

	return 0;
}

  • 所谓的free释放,到底在做什么?
  • 改变指针,和目标堆空间的对应关系
  • 所有的“关系”都是需要数据去维护的。
  • 那为什么不能设置为NULL呢?
    • 没有必要。你没有权力进行访问,对系统不会造成影响,你非要去访问,需要手动释放。
    • 可以在free之后,带上p=NULL;
int main()
{
	char* p = (char*)malloc(sizeof(char) * 20);

	printf("before %p \n", p);
	free(p);
	printf("after %p \n", p);
	// 前后地址相同 ,但是free后就不能在访问堆空间这个地址了
	// 那为什么不能设置为NULL呢?

	return 0;
}
  • 内存管理的本质是什么?
    • 空间什么时候申请,申请多少,什么时候释放,释放多少,释放多少的问题。
  • 在c语言中:程序员+场景 = 内存管理