内存管理总结

公众号:CppCoding

内存管理基础
作用域
C语言变量的作用域分为:
- 代码块作用域{代码块是{}之间的一段代码}
- 函数作用域
- 文件作用域
局部变量
局部变量也叫auto自动变量,一般情况下代码块{}内部定义的变量都是自动变量,其特点:
- 在一个函数内定义,只在函数范围内有效
- 在复合语句中定义,只在复合语句中有效
- 随着函数调用的结束或复合语句的结束局部变量的声明声明周期结束
- 如果没有赋初值,内容为随机

#include <stdio.h>

void test()
{
	//auto写不写是一样的
	//auto只能出现在{}内部
	auto int b = 10; 
}

int main(void)
{
	//b = 100; //err, 在main作用域中没有b

	if (1)
	{
		//在复合语句中定义,只在复合语句中有效
		int a = 10;
		printf("a = %d\n", a);
	}

	//a = 10; //err离开if()的复合语句,a已经不存在
	
	return 0;
}

静态(static)局部变量
static局部变量的作用域也是在定义的函数内有效
static局部变量的生命周期和程序运行周期一样,同时static局部变量的值只初始化一次,但是可以多次赋值
static局部变量若未赋以初值,则由系统自动赋值:数值型变量自动赋初值0,字符型变量赋空字符

#include <stdio.h>

void fun1()
{
	int i = 0;
	i++;
	printf("i = %d\n", i);
}

void fun2()
{
	//静态局部变量,没有赋值,系统赋值为0,而且只会初始化一次
	static int a;
	a++;
	printf("a = %d\n", a);
}

int main(void)
{
	fun1();
	fun1();
	fun2();
	fun2();
	
	return 0;
}

全局变量
在函数外定义,可被本文件及其他文件中的函数所共用,若其他文件中的函数调用此变量,须用extern声明
全局变量的生命周期和程序运行周期一样
不同文件的全局变量不可重名
静态全局变量()static
在函数外定义,作用范围被限制在所定义的文件中
不同文件静态全局变量可以重名,但作用域不冲突
static全局变量的生命周期和程序运行周期一样,同时static全局变量的值只初始化一次
extern全局变量声明
extern int a;声明一个变量,这个变量在别的文件中已经定义,这里只是声明,而不是定义
https://blog.csdn.net/he__yuan/article/details/81516264
全局函数和静态函数
在C语言中函数默认都是全局的,使用关键字static可以将函数声明为静态,函数定义为static就意味着这个函数只能在定义这个函数的文件使用,其他文件不可以使用,即使在其他文件中声明这个函数都没有用

对于不同文件中的static函数名字可以相同

注意:
- 允许在不同的函数中使用相同的变量名,他们代表不同的对象,分配不同的单元,互不干扰
- 同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用
- 所有的函数默认都是全局的,意味着所有的函数都不能重名,但是如果是static函数,那么作用域是文件级的,所以不同的文件static函数名都是可以相同的

在这里插入图片描述
内存布局

内存分区
C代码经过预处理、编译、汇编、链接后生成一个可执行程序。

代码区:
存放CPU执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,使其只读的原因是防止程序意外的修改了他的指令。另外代码区还规划了局部变量的相关信息。
全局初始化数据区/静态数据区(data段):
该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。
未初始化数据区(bss)
存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化0或者为空(NULL)。
程序在加载到内存前,代码区和全局区(data和bss)的大小是固定的,程序运行期间不能改变。然后,运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)区之外,还额外增加栈区和堆区。在这里插入图片描述这五部分内容:

  • 代码区(text segment)
    加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的
  • 未初始化数据区(bss)
    加载的是可执行BSS段,位置可以分开也可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的声明周期为整个程序运行过程
  • 全局初始化数据区/静态数据区(data segment)
    加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生命周期为整个程序运行过程
  • 栈区(stack)
    栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间
  • 堆区(heap)
    堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配,堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

存储类型总结
在这里插入图片描述
存储类型总结内存操作函数
memset()

#include <string.h>
void *memset(void *s,int c,size_t n);
功能:将s的内存区的前n个字节以参数c填入
参数:
	s:需要操作内存s的首地址
	c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
	n:指定需要设置的大小
返回值:s的首地址

memcpy()

#include <string.h>
void *memcpy(void *dest,const void *src,size_t n);
功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
	dest:目的内存首地址
	src:源内存首地址,注意:dest和src所指的内存空间不可重叠
	n:需要拷贝的字节数
返回值:dest的首地址

memmove()
memmove()功能用法和memcpy()一样,区别在于:dest和src所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比memcpy()低一些

memcmp()

#include <string.h>
int memcmp(const void* s1,const void* s2,size_t n);
功能:比较s1和s2所指向内存区域的前n个字节
参数:
	s1:内存首地址1
	s2:内存首地址2
	n:需比较的前n个字节
返回值:
	相等:=0
	大于:>0
	小于:<0
	```
**堆区内存分配和释放**
malloc()
```java
#include <stdlib.h>
void *malloc(size_t size);
功能:在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。分配的内存空间内容不确定,一般使用memset初始化。
参数:
	size:需要分配内存大小(单位:字节)
返回值:
成功:分配空间的起始地址
失败:NULL

free()

#include <stdlib.h>
void free(void *ptr);
功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,指向被释放区域的首地址。对同一内存空间多次释放会出错。
参数:
ptr:需要释放空间的首地址,被释放区应是由malloc函数所分配的区域。
返回值:无

https://blog.csdn.net/qq_42322103/article/details/99089546

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值