C/C++-内存
迪丽瓦拉
2024-04-13 04:03:34
0

C/C++-内存

  • CPU与内存
  • 数据与地址
  • 内存分区
    • 静态/动态内存
  • 内存泄漏
  • 缓存与缓冲区
    • FILE 与缓冲区

CPU与内存

程序加载到内存后,操作系统会给不同的内存指定不同的权限,拥有读取和执行权限的内存块是代码,拥有读取和写入权限的内存块是数据
CPU一般通过地址来取得内存中的代码和数据
CPU访问内存时需要的是地址,而不是变量名和函数名,当源文件被编译和链接成可执行程序后,都会被替换成地址
(变量名是数据,函数、字符串名、数组名是代码块或数据的首地址)

程序在硬盘中,要载入内存才能运行
CPU只能从内存中读取数据和指令
对于CPU,内存时存放指令和数据,并不能完成计算功能
计算机:硬盘->内存->CPU:缓存->>
CPU=运算器+寄存器+缓存

寄存器:一个寄存器能够存储32/64位的数据,CPU一般由上百个寄存器(多少位的计算机,表示的是寄存器是多少位的)。寄存器用来进行数学运算或流程控制
缓存:内存读取速度小于CPU,因此将频繁使用的数据暂时读取到缓存区。CPU只能从缓存中读取到部分数据,对于使用不是很频繁的数据会直接从内存中读取。

数据与地址

数据以二进制的形式保存在内存中,字节是最小的可操作单位
在内存管理中,为每个字节分配了一个编号,使用该字节时,只要知道编号就可以,这个编号,就是地址

获取地址

&data,(data可以是数值、字符)

内存分区

  • 栈区

编译器自动分配
局部变量、形参、返回值,const 定义的局部变量也存放在栈区

操作系统自动管理
栈区上的内容只在函数范围内存在,函数结束自动销毁
栈区内存地址:由高到低,即后定义的变量地址低于先定义的变量地址
先进后出
属于动态内存分配

函数中定义的局部变量按照先后定义的顺序一次压入栈中,即,相邻变量的地址之间不会存在其他变量

  • 堆区

程序员分配内存和释放,若开发人员不释放,程序结束时有系统回收
malloc() free()
new delete

堆区内存地址:由低到高
大小由系统内存/虚拟内存上限决定
属于动态内存分配

注意:后申请的内存空间并不一定在先申请的内存空间的后面,因为先申请的内存空间一旦释放,后申请的内存空间则会利用先前被释放的内存,从而导致先后分配的内存空间在地址上不存在先后关系
堆中存储的数据若未释放,则生命周期等同于程序的生命周期
由于系统是通过链表来存储空闲的内存地址,因此堆数据时不连续的内存空间

对于堆分配,操作系统有一个记录空闲内存地址的链表,当申请内存时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的内存空间分配给程序。另外,对于大多数系统,会在这块内存空间中的首地址出记录本次分配的大小,这样使用delete才能正确释放内存空间。由于找到的堆节点的大小不一定正好等于申请的大小,系统会自动将多余的部分重新放入空间链表

// c 用malloc// 注意指针的类型转换
char* p1=(char*)malloc(10);
free(p1);
//free(p)并不能改变指针p的值,p依然指向以前的内存,为了防止再次使用该内存,建议将p的值手动置为NULL// C++ 用new
char* p2=new char[10];
delete[] p2
  • 全局(静态)区

未初始化全局(静态)区 .bass
已初始化全局(静态)区 .data

在编译期间就能确定 存储大小的变量 的存储区,
包括全局变量,静态变量(包括静态全局变量、静态局部变量)
属于静态内存分配

.bass

读写
未初始化的全局变量或未初始化的静态变量
初始化为0的全局变量或初始化为0的静态变量
.bass段不占用可执行文件空间,由操作系统初始化

.data

读写
已初始化的全局变量 (但初始化不为0的)
已初始化的静态变量 (但初始化不为0的)
.data段占用可执行文件空间,由程序初始化

  • 常量区

.rodata

字符、字面值、字符串等常量
const 修饰的全局变量(const修饰的局部变量存放在栈区)
运行期间,常量区的内容不可被修改
属于静态内存分配

  • 代码区

存放程序的代码 .txt
只读
属于静态内存分配
不可修改

静态/动态内存

  • 静态内存分配

代码区常量区全局数据区的内存在程序启动时就已经分配好了,大小固定、不能由程序分配或释放,只能等到程序运行结束由操作系统回收

  • 动态内存分配

栈区堆区的内存在程序运行期间可以根据实际需求来分配和释放,不用在程序刚启动时就备足所有内存

内存泄漏

一块内存没有被指针指向它,(程序和内存失去了联系,再无法对他进行任何操作),这块内存直到程序结束被系统回收

缓存与缓冲区

内存中用于临时保存输入输出数据
缓冲区 :内存空间的一部分
作用: 减少磁盘的读写次数 ;
分类

输入缓冲区/输出缓冲区
根据数据刷新时机

全缓冲:缓冲区被填满时(一般是对硬盘的读写)
行缓冲:遇到换行符时 (标准I/O),prinf(“\n”),scanf回车
无缓冲: getche(),getch() 这两个函数没有缓冲
(程序结束的时候 也会刷新缓冲区)
(输出之后有输入的操作,也会刷新缓冲区)

注意:不同的操作系统对缓冲区的定义时不同的,printf在windows下无需缓冲,在linux下有缓冲

不刷新缓冲区的例子

// 这个例子,进入死循环
// 缓冲区没有被填满、没有遇到换行符、程序没有结束,所以标准输出始终没有显示字符串
int main()
{printf("hello world"); // 只要加上printf("hello world\n") 立即刷新while (1);return 0;
}

刷新

行缓冲,全缓冲:缓冲区满时自动刷新
行缓冲:遇到’\n’时,自动刷新
关闭文件时自动刷新
程序关闭时自动刷新
使用刷新函数

清空输出缓冲区

fflush(stdout) // 一般用于linux系统下,清空标准输出缓冲区,清空屏幕缓冲区

清空输入缓冲区

int c
while (c=getchar()!='\n' && c!=EOF)

FILE 与缓冲区

FILE *fp;FILE 结构体
int cn // 剩余字符,缓冲区中还有多少个字符未被读取
char *ptr //下一个要被读取的字符的地址
char *base // 缓冲区的基地址
int flag // 读写状态标志位
int fd // 文件描述符

FILE是文件缓冲区的结构体,fp是指向文件缓冲区的指针
缓冲区的刷新表示将 缓冲区的指针变为缓冲区的基地址

相关内容