复习笔记2:C语言的内存管理,用户栈的代码实现

概要

本人正在准备24年秋招,现记录从现在开始的复习笔记。
本节是笔记2:C语言的内存管理,用户栈的代码实现学习记录。

内存分区

C语言中的内存分区从上往下依次是:

  • 栈区:存放局部变量、函数传递的参数、函数返回值及返回地址、const定义的局部变量。
  • 堆区:一块可分配内存,堆区由程序员分配内存和释放
  • .bss段:存放未初始化或初始值为0的全局变量,特点是不占用可执行文件空间,.bss段通常需要进行清零操作。
  • .data段:存放有初始值的全局变量、静态变量,其中const修饰的变量存放在.rodata段。.data段涉及代码重定位,.data的初始值需要被拷贝至运行地址才能正常访问,在C标准函数__main中实现拷贝。
  • 常量区:const修饰的全局变量或静态变量,STM32中会将其保存在FLASH中,也叫.rodata段。
  • 代码段:存放程序的代码,即被转化为机器码的二进制数据。
  • 空字节段:从0地址开始直到代码段的区域, 是没有用到的字节区域,一般存放指向NULL的空指针

malloc

一般使用方式如下:

struct stack_t *p = (struct stack_t *)malloc(sizeof(struct stack_t));

申请一块想要的内存大小并返回首地址,Linux中内部是通过sbrk函数向内核申请内存来实现的,其内部还需要实现查找内存块、各种内存块链表操作等,这里不细说。

free

一般使用方式如下:

free(p);					//记得释放内存

free只能释放malloc返回的指针,否则会报错,free内部操作较为复杂,除了将已申请的malloc内存块插入到空闲内存块链表以外,还要完成相邻内存块合并等操作来最大限度利用空间,最大程度减少内存碎片。
malloc而不进行free将会导致内存泄漏,导致之前被申请的内存块处于空闲但不能被再次申请

栈操作通常由编译器自己完成,通常在程序需要调用函数跳转时进行栈的操作,栈操作有push入栈和pop出栈。可以通过编写简单的函数调用程序,然后观察其反汇编代码查看编译器入栈和出栈的过程。这里不细说,本节重点在下文。

用户栈的代码实现

用户栈分类

  • 数组栈:栈内存是数组,一块连续的内存,大小自定义指定,数组的索引作为栈顶指针。
  • 链表栈:栈内存需要动态分配,一个数据对应一个结点,大小视程序而定,不需要自己指定,以头结点的node属性作为索引。
#define STACK_MAX_SIZE	1*1024		//设置栈大小

#define ARRAY_STACK		1			//指定使用数组栈
#define LIST_STACK		2			//指定使用链表栈
#define STACK_TYPE ARRAY_STACK   	//指定使用哪种栈类型

//构建自定义栈结构体
typedef struct stack_t{
#if STACK_TYPE == ARRAY_STACK
    int stack_data[STACK_MAX_SIZE]; //栈内存
    int stack_sp;                   //栈顶指针
#elif STACK_TYPE == LIST_STACK
    int data;						//栈结点数据
    struct stack_t *next;			//指向下一个结点
#endif
}stacks;

新建栈和初始化栈

  • 数组栈:定义一个栈结构体,编译器自动分配内存,再把栈顶指针即数组索引值设置为0即可。
  • 链表栈:定义一个栈结构体,编译器自动分配内存,再把栈顶指针即栈链表头部next设置为NULL即可。
void StackUserInit(struct stack_t *pStack_User)
{
#if STACK_TYPE == ARRAY_STACK
	pStack_User->stack_sp = 0;
#elif STACK_TYPE == LIST_STACK
	pStack_User->next = NULL;
#endif
}

Push实现

  • 数组栈Push:先填入数据,再栈顶指针偏移。
  • 链表栈Push:先申请一个新的数据结点的内存并将数据填入,再插入到栈链表头部结点next属性之后。
/********************************************************
* Description   : 入栈
* @pStack_User  : 目标栈的指针
* @data  		: 入栈数据
* Return        : 0:入栈成功    
*				 -1:入栈失败,可能是栈满了
**********************************************************/
int Push(struct stack_t *pStack_User, int data)
{
#if STACK_TYPE == ARRAY_STACK
	/* 如果栈还没满 */
	if(pStack_User->stack_sp != STACK_MAX_SIZE){
		pStack_User->stack_data[pStack_User->stack_sp] = data;	//数据入栈
		pStack_User->stack_sp++;								//栈顶指针偏移
		return 0;
	}
	else{
		/* 栈已满 */
		return -1;
	}
#elif STACK_TYPE == LIST_STACK
	struct stack_t *p = (struct stack_t *)malloc(sizeof(struct stack_t));
	if(p){
		/* 先储存数据 */
		p->data = data;
		/* 再插入链表 */
		p->next = pStack_User->next;
		pStack_User->next = p;
		return 0;
	}
	else{
		/* 内存申请失败,入栈失败 */
		return -1;
	}
#endif
}

Pop实现

  • 数组栈Pop:先将栈顶指针偏移,再返回数据。
  • 链表栈Pop:先将当前栈顶指针next属性处数据储存,再删除该结点并释放内存,最后返回数据。
/********************************************************
* Description   : 出栈
* @pStack_User  : 目标栈的指针
* @data  		: 数据存储指针
* Return        : 0:入栈成功    
*				 -1:入栈失败,可能是栈满了
**********************************************************/
int Pop(struct stack_t *pStack_User, int *data)
{
#if STACK_TYPE == ARRAY_STACK
	/* 如果栈不是空的 */
	if(pStack_User->stack_sp != 0){
		pStack_User->stack_sp--;								//栈顶指针偏移
		*data = pStack_User->stack_data[pStack_User->stack_sp];	//数据出栈
		return 0;
	}
	else{
		/* 栈是空的 */
		return -1;
	}
#elif STACK_TYPE == LIST_STACK
	struct stack_t *p = pStack_User->next;	//获得头结点后第一个栈结点指针
	/* 如果栈不是空的 */
	if(p){
		pStack_User->next = p->next;//删除该栈结点
		*data = p->data;			//返回数据
		free(p);					//记得释放内存
		return 0;
	}
	else{
		/* 栈是空的 */
		return -1;
	}
#endif
}

用户栈操作注意

  • PushPop都需要判断栈满与栈空的情况,代码中均已体现。

完整工程代码

  • 所有代码均已进行测试,在windows上可以正常运行。
#include "stdio.h"
#include "stdlib.h"

#define STACK_MAX_SIZE	1*1024		//设置栈大小

#define ARRAY_STACK		1			//指定使用数组栈
#define LIST_STACK		2			//指定使用链表栈
#define STACK_TYPE ARRAY_STACK   	//指定使用哪种栈类型

//构建自定义栈结构体
typedef struct stack_t{
#if STACK_TYPE == ARRAY_STACK
    int stack_data[STACK_MAX_SIZE]; //栈内存
    int stack_sp;                   //栈顶指针
#elif STACK_TYPE == LIST_STACK
    int data;						//栈结点数据
    struct stack_t *next;			//指向下一个结点
#endif
}stacks;

/********************************************************
* Description   : 入栈
* @pStack_User  : 目标栈的指针
* @data  		: 入栈数据
* Return        : 0:入栈成功    
*				 -1:入栈失败,可能是栈满了
**********************************************************/
int Push(struct stack_t *pStack_User, int data)
{
#if STACK_TYPE == ARRAY_STACK
	/* 如果栈还没满 */
	if(pStack_User->stack_sp != STACK_MAX_SIZE){
		pStack_User->stack_data[pStack_User->stack_sp] = data;	//数据入栈
		pStack_User->stack_sp++;								//栈顶指针偏移
		return 0;
	}
	else{
		/* 栈已满 */
		return -1;
	}
#elif STACK_TYPE == LIST_STACK
	struct stack_t *p = (struct stack_t *)malloc(sizeof(struct stack_t));
	if(p){
		/* 先储存数据 */
		p->data = data;
		/* 再插入链表 */
		p->next = pStack_User->next;
		pStack_User->next = p;
		return 0;
	}
	else{
		/* 内存申请失败,入栈失败 */
		return -1;
	}
#endif
}

/********************************************************
* Description   : 出栈
* @pStack_User  : 目标栈的指针
* @data  		: 数据存储指针
* Return        : 0:入栈成功    
*				 -1:入栈失败,可能是栈满了
**********************************************************/
int Pop(struct stack_t *pStack_User, int *data)
{
#if STACK_TYPE == ARRAY_STACK
	/* 如果栈不是空的 */
	if(pStack_User->stack_sp != 0){
		pStack_User->stack_sp--;								//栈顶指针偏移
		*data = pStack_User->stack_data[pStack_User->stack_sp];	//数据出栈
		return 0;
	}
	else{
		/* 栈是空的 */
		return -1;
	}
#elif STACK_TYPE == LIST_STACK
	struct stack_t *p = pStack_User->next;	//获得头结点后第一个栈结点指针
	/* 如果栈不是空的 */
	if(p){
		pStack_User->next = p->next;//删除该栈结点
		*data = p->data;			//返回数据
		free(p);					//记得释放内存
		return 0;
	}
	else{
		/* 栈是空的 */
		return -1;
	}
#endif
}

void StackUserInit(struct stack_t *pStack_User)
{
#if STACK_TYPE == ARRAY_STACK
	pStack_User->stack_sp = 0;
#elif STACK_TYPE == LIST_STACK
	pStack_User->next = NULL;
#endif
}

void PrintStack(struct stack_t *pStack_User)
{
	int buf;
	int i=0;

	while(Pop(pStack_User, &buf) == 0)
	{
		printf("Stack_User_Pop top%d: %d\r\n",i++, buf);
	}
}


int main(int argc, char *argv[])
{
	struct stack_t Stack_User;
	StackUserInit(&Stack_User);

	Push(&Stack_User, 100);
	Push(&Stack_User, 200);
	Push(&Stack_User, 300);
	Push(&Stack_User, 400);
	Push(&Stack_User, 500);

	PrintStack(&Stack_User);

    printf("完成!\r\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值