计算机中的内存管理
类型
内核
指操作系统内核,是操作系统的核心组件,负责管理系统资源
堆区
主要用于动态内存分配(malloc/free)
栈区
用于存储程序执行期间的临时变量,如函数调用的参数、局部变量和返回地址。栈的内存分配和回收是自动的。
数据区
数据区通常用于存储全局变量和静态变量。这个区域可以进一步分为初始化数据区(用于存储初始
化的全局变量和静态变量)和未初始化数据区(也称为 BSS 段,用于存储未初始化的全局变量和
静态变量)。
文本区
文本区或代码区用于存储程序的执行代码,也就是 CPU 执行的机器指令。
栈区
存放的数据类型
1.局部变量
函数内的局部变量通常存储在栈上。当函数被调用时,其局部变量被创建并推入栈中,函数执行完
毕后,这些变量随栈帧(stack frame)一起被销毁
2.函数参数
函数的形参和返回值通常存放在栈区
3.返回地址
栈区在函数进行函数调用时,会保存调用的函数的入口地址(保护现场)
保护现场和恢复现场的规则:先入后出,后入先出
4.递归调用的嵌套关系
在递归函数中,每次递归调用都会生成一个新的栈帧,包含该调用的局部变量和返回地址,这些栈
帧在递归调用返回时被销毁
数据栈(通常的“栈”)和系统栈(调用栈)的区别与联系
用途
数据栈:用于存储程序运行时的临时数据,如表达式求值、函数调用的中间结果等
系统栈:用于跟踪函数调用的顺序和状态,包括存储局部变量、参数、返回地址和函数的执行环境
控制方式
数据栈:由程序员在代码中通过栈操作函数控制
系统栈:由操作系统和运行时环境自动管理,例如在函数调用和返回时自动压栈和出栈
作用域
数据栈:用于存储不同作用域的数据,但通常用于局部作用域
系统栈:用于存储函数调用的作用域信息,每个函数调用都有自己的栈帧
相同之处
LIFO(后进先出)原则:无论是数据栈还是系统栈,都遵循 LIFO 的原则,即最后进入的元素会第一个被移除
栈操作:两者都使用类似的基本操作,如 push
(压栈)和 pop
(出栈),来添加和移除元素
内存管理:它们都涉及到内存的分配和释放。数据栈和系统栈在添加元素时分配内存,在移除元素时释放内存
数据结构:从数据结构的角度看,它们都是栈这种抽象数据类型的实现,无论是在硬件层面还是软件层面
数据栈(栈)
栈结构
只允许从一端进行数据的插入和删除的线性的存储结构
数据插入:入栈,压栈
数据出栈:出栈,弹栈
数据栈分类
顺序栈:用数组实现
链式栈:用链表实现
1.创建栈
Stack_t *create_stack()
{
Stack_t *pnode = malloc(sizeof(Stack_t));
if(NULL == pnode)
{
return NULL;
}
pnode->ptop = NULL;
pnode->clen = 0;
return pnode;
}
2.入栈
int push_stack(Stack_t *pnode,DataType data)
{
SNode_t *psnote = malloc(sizeof(SNode_t));
if(NULL == psnote)
{
return -1;
}
psnote->data = data;
psnote->pnext = pnode->ptop;
pnode->ptop = psnote;
pnode->clen++;
return 0;
}
3.出栈
int pop_stack(Stack_t *pnode,DataType *data)
{
if(is_empty_stack(pnode))
{
return 0;
}
*data = pnode->ptop->data;
SNode_t *pdel = pnode->ptop;
pnode->ptop = pdel->pnext;
free(pdel);
pnode->clen--;
return 0;
}
4.清空栈
void clear_stack(Stack_t *pnode)
{
if(is_empty_stack(pnode))
{
return;
}
SNode_t *psnote = pnode->ptop;
while(psnote != NULL)
{
SNode_t*ptmp = psnote;
psnote = psnote->pnext;
free(ptmp);
}
pnode->ptop = NULL;
pnode->clen = 0;
}
5.判空
int is_empty_stack(Stack_t *pnode)
{
return NULL == pnode->ptop;
}
6.获取栈顶元素
int get_stack_top(Stack_t *pnode,DataType *data)
{
if(is_empty_stack(pnode))
{
return 0;
}
*data = pnode->ptop->data;
//SNode_t *psnote = pnode->ptop;
return 0;
}
7.销毁栈
void destory_stack(Stack_t *pnode)
{
clear_stack(pnode);
free(pnode);
}
栈结构:满增栈 满减栈 空减栈 空增栈
满栈、空栈:
满栈的入栈顺序,先移动栈顶,再入栈数据
空栈的入栈顺序:先入栈一个数据,再移动栈顶
出栈顺序:先移动的栈顶,再移出数据
增栈、减栈:
增栈:无论是满栈还是空栈,加入数据后,栈顶是由内存低地址移向高地址就是增栈
减栈:栈顶由高内存地址移项低内存地址
队列
允许从一端插入数据,另一端删除数据的线性的存储结构
队尾:插入数据 入队
队头:取出数据 出队
先进先出,后进后出 FIFO
管道的本质就是一个队列
缓冲区:匹配高速设备和低速设备之间的效率问题
队列就可以作为缓冲区来解决高低速设备之间传输时的效率
队列和栈之间的区别
数据访问原则:
栈:遵循后进先出(LIFO,Last-In-First-Out)的原则,即最后添加到栈的元素会第一个被移除。
队列:遵循先进先出(FIFO,First-In-First-Out)的原则,即最先添加到队列的元素会第一个被移除。
操作方式
栈:主要操作包括 push
(在栈顶添加元素)和 pop
(从栈顶移除元素)。
还有一个 peek
或 top
操作,用来查看栈顶元素但不移除它
队列:主要操作包括 enqueue
(在队尾添加元素)和 dequeue
(从队首移除元素)。
同样有 front
操作,用来查看队首元素但不移除它。
使用场景:
栈:常用于处理具有递归结构的问题,如函数调用堆栈、表达式求值、括号匹配、回溯算法等。
队列:常用于处理消息队列、任务调度、广度优先搜索算法、打印机任务排队等场景。
内存利用率:
栈:由于其 LIFO 的特性,栈的内存利用率通常较高,因为栈顶的内存可以快速地被重新利用。
队列:可能需要更多的内存,因为元素在队列中停留的时间可能更长,直到它们到达队首被移除。
队列分类:
顺序队列:数组 ------>存在假溢出问题,所以一般使用顺序队列的时候用循环顺序队列
链式队列:链表
1.创建队列
Queue_t *create_queue()
{
Queue_t *qnode = malloc(sizeof(Queue_t));
if(NULL == qnode)
{
return NULL;
}
qnode->pfront = NULL;
qnode->ptail = NULL;
qnode->clen = 0;
return qnode;
}
2.入队
int push_queue(Queue_t *qnode,QDataType data)
{
QNode_t *plink = malloc(sizeof(QNode_t));
if(NULL == plink)
{
return -1;
}
plink->pnext = NULL;
plink->data = data;
if(is_empty_queue(qnode))
{
qnode->pfront = plink;
qnode->ptail = plink;
qnode->clen = 1;
}
else
{
qnode->ptail->pnext = plink;
qnode->ptail = plink;
qnode->clen++;
}
return 0;
}
3.出队
int pop_queue(Queue_t *qnode,QDataType *data)
{
if(is_empty_queue(qnode))
{
return 0;
}
QNode_t *p = qnode->pfront;
if(p->pnext == NULL)
{
qnode->pfront = NULL;
qnode->ptail = NULL;
qnode->clen = 0;
}
else
{
qnode->pfront = p->pnext;
qnode->clen--;
}
*data = p->data;
free(p);
return 0;
}
4.清空队列
void clear_queue(Queue_t *qnode)
{
while(!(is_empty_queue(qnode)))
{
QNode_t *p = qnode->pfront;
qnode->pfront = p->pnext;
free(p);
if(qnode->pfront == NULL)
{
qnode->ptail = NULL;
qnode->clen = 0;
}
}
}
5.判空
int is_empty_queue(Queue_t *qnode)
{
return NULL == qnode->pfront;
}
6.获取栈顶元素
int get_queue_front(Queue_t *qnode,QDataType *data)
{
if(is_empty_queue(qnode))
{
return 0;
}
*data = qnode->pfront->data;
return 0;
}
7.销毁栈
void destory_queue(Queue_t *qnode)
{
if(qnode != NULL)
{
clear_queue(qnode);
free(qnode);
qnode = NULL;
}
}