1.二级指针
1.在被调函数中需要修改主调函数的指针变量时,需传递其指针地址;
2.指针数组的数组名作为参数传递时;
2.内核链表
(1)定义
内核链表:本质为循环双向链表,其没有数据域
存储形式:将节点嵌于结构体内(数据中),而不是将数据存于节点内;
(2)内核链表的相关宏
offset_of
作用:获取节点到结构体首地址的偏移量;
原理:假设结构体的首地址为0,则节点的首地址即为其到结构体首地址的偏移量;
container_of
作用:通过偏移量,获取结构体的首地址;
原理:用节点首地址减去偏移量;
一般定义内核链表时,将节点置于结构体首部,此时节点首地址即为结构体首地址;
3.栈
先进后出的存储方式;
满栈、空栈:栈顶是否存储数据;
增栈:入栈时,栈顶由内存地地址-->内存高地址,出栈反之;
减栈:栈顶的运动方式与增栈相反;
对象:包含头地址与元素个数;
节点:包含元素与指向下一个节点的指针;
栈相关的代码练习:
typedef int Data_type_t;
typedef struct stnode
{
Data_type_t data;
struct stnode *pnext;
}Stnode_t;
typedef struct stack
{
Stnode_t *ptop;
int clen;
}Stack_t;
Stack_t *c_stack()
{
Stack_t *stack = malloc(sizeof(Stack_t));
if(NULL == stack)
{
printf("error\n");
return NULL;
}
stack->ptop = NULL;
stack->clen = 0;
return stack;
}
int is_empty_stack(Stack_t *stack)
{
return NULL == stack->ptop ;
}
void stack_for_each(Stack_t *stack)
{
Stnode_t *ptmp = stack->ptop;
while (ptmp)
{
printf("%d ", ptmp->data);
ptmp = ptmp->pnext;
}
puts("");
}
//入栈
int push_stack(Stack_t *stack, Data_type_t data)
{
Stnode_t *pinst = malloc(sizeof(Stnode_t));
if(NULL == pinst)
{
printf("error\n");
return -1;
}
pinst->pnext =NULL;
pinst->data = data;
if(is_empty_stack(stack))
{
stack->ptop = pinst;
}
else
{
pinst->pnext = stack->ptop;
stack->ptop = pinst;
}
stack->clen++;
return 0;
}
//出栈
int pop_stack(Stack_t *stack, Data_type_t *pdata)
{
if(is_empty_stack(stack))
{
return -1;
}
Stnode_t *ptmp = stack->ptop;
stack->ptop = ptmp->pnext;
if(pdata)
{
*pdata = ptmp->data;
printf("pop : %d\n", *pdata);
}
free(ptmp);
stack->clen--;
return 0;
}
//获取栈元素
int get_top_stack(Stack_t *stack, Data_type_t *pdata)
{
if(is_empty_stack(stack))
{
return -1;
}
if(pdata && stack->ptop->data)
{
*pdata = stack->ptop->data;
printf("top : %d\n", *pdata);
return *pdata;
}
}
//销毁
void destroy_stack(Stack_t **stack, Data_type_t *pdata)
{
if(is_empty_stack(*stack))
{
return ;
}
while ((*stack)->ptop)
{
pop_stack(*stack, pdata);
}
free(*stack);
stack =NULL;
return ;
}
4.队列
(1)链式队列
定义:允许从一段进行数据的插入,从另一端进行数据的删除的线性存储结构;
插入:称为入队,插入的一端即为队尾;
删除:称为出队,删除的一端即为队头;
特点:先进先出;
应用:应用于缓存数据;
对象:包含头地址、尾地址与元素个数;
节点:包含元素个数与指向下一个节点的指针;
代码练习
#ifndef __LQUEUE_H__
#define __LQUEUE_H__
typedef int Data_type_t;
typedef struct lqnode
{
Data_type_t data;
struct lqnode *pnext;
}Lqnode_t;
typedef struct lqueue
{
Lqnode_t *phead;
Lqnode_t *ptail;
int clen;
}Lqueue_t;
extern Lqueue_t *c_lqueue();
extern int is_lqueue_empty(Lqueue_t *lqueue);
extern int insert_lqueue_tail(Lqueue_t *lqueue, Data_type_t data);
extern void lqueue_for_each(Lqueue_t *lqueue);
extern int delet_lqueue_head(Lqueue_t *lqueue, Data_type_t *pdata);
extern int get_top_lqueue(Lqueue_t *lqueue, Data_type_t *hdata);
extern void destroy_lqueue(Lqueue_t **lqueue);
#endif
#include <stdio.h>
#include <stdlib.h>
#include "lqueue.h"
//创建
Lqueue_t *c_lqueue()
{
Lqueue_t *lqueue = malloc(sizeof(Lqueue_t));
if(NULL == lqueue)
{
printf("malloc error\n");
return NULL;
}
lqueue->phead = NULL;
lqueue->ptail = NULL;
lqueue->clen = 0;
return lqueue;
}
//判断队列是否为空
int is_lqueue_empty(Lqueue_t *lqueue)
{
return NULL == lqueue->phead;
}
//遍历
void lqueue_for_each(Lqueue_t *lqueue)
{
if(is_lqueue_empty(lqueue))
{
return ;
}
Lqnode_t *ptmp = lqueue->phead;
while(ptmp)
{
printf("%d ", ptmp->data);
ptmp = ptmp->pnext;
}
puts("");
}
//入队(尾插)
int insert_lqueue_tail(Lqueue_t *lqueue, Data_type_t data)
{
Lqnode_t *pinset = malloc(sizeof(Lqnode_t));
if(NULL == pinset)
{
printf("malloc error\n");
return -1;
}
pinset->pnext = NULL;
pinset->data = data;
if(is_lqueue_empty(lqueue))
{
lqueue->phead = pinset;
lqueue->ptail = pinset;
}
else
{
lqueue->ptail->pnext = pinset;
lqueue->ptail = pinset;
}
lqueue->clen++;
return 0;
}
//出队(头删)
int delet_lqueue_head(Lqueue_t *lqueue, Data_type_t *pdata)
{
if(is_lqueue_empty(lqueue))
{
return -1;
}
Lqnode_t *pdet = lqueue->phead;
if(NULL != pdata)
{
*pdata = lqueue->phead->data;
}
lqueue->phead = pdet->pnext;
if(NULL == pdet->pnext)
{
lqueue->ptail = NULL;
}
free(pdet);
lqueue->clen--;
return 0;
}
//获取队头元素
int get_top_lqueue(Lqueue_t *lqueue, Data_type_t *hdata)
{
if(is_lqueue_empty(lqueue))
{
return -1;
}
if(hdata && lqueue->phead->data)
{
*hdata = lqueue->phead->data;
printf("top : %d\n", *hdata);
}
return 0;
}
//销毁
void destroy_lqueue(Lqueue_t **lqueue)
{
while((*lqueue)->phead)
{
delet_lqueue_head(*lqueue, NULL);
}
free(*lqueue);
*lqueue = NULL;
}
int main(void)
{
Lqueue_t *lqueue = c_lqueue();
if(NULL == lqueue)
{
printf("malloc error\n");
return -1;
}
Data_type_t hdata;
insert_lqueue_tail(lqueue, 1);
insert_lqueue_tail(lqueue, 2);
insert_lqueue_tail(lqueue, 3);
insert_lqueue_tail(lqueue, 4);
insert_lqueue_tail(lqueue, 5);
lqueue_for_each(lqueue);
delet_lqueue_head(lqueue, &hdata);
lqueue_for_each(lqueue);
get_top_lqueue(lqueue, &hdata);
destroy_lqueue(&lqueue);
return 0;
}
(2)循环队列
代码练习
#define SEQQUE_MAX_LEN 10
typedef int Data_type_t;
typedef struct seqque
{
Data_type_t *pbase;
int head;
int tail;
}Seqque_t;
Seqque_t *create_seqque()
{
Seqque_t *psq = malloc(sizeof(Seqque_t));
if (NULL == psq)
{
printf("malloc error\n");
return NULL;
}
psq->head = 0;
psq->tail = 0;
psq->pbase = malloc(sizeof(Data_type_t) * SEQQUE_MAX_LEN);
if (NULL == psq->pbase)
{
printf("malloc error\n");
return NULL;
}
return psq;
}
int is_full_seqque(Seqque_t *psq)
{
if ((psq->tail+1)%SEQQUE_MAX_LEN == psq->head)
{
return 1;
}
return 0;
}
int is_empty_seqque(Seqque_t *psq)
{
if (psq->head == psq->tail)
{
return 1;
}
return 0;
}
int push_seqque(Seqque_t *psq, Data_type_t data)
{
if (is_full_seqque(psq))
{
printf("seqque is full\n");
return -1;
}
psq->pbase[psq->tail] = data;
psq->tail = (psq->tail+1) % SEQQUE_MAX_LEN;
return 0;
}
void seqque_for_each(Seqque_t *psq)
{
for (int i = psq->head; i != psq->tail; i = (i+1)%SEQQUE_MAX_LEN)
{
printf("%d ", psq->pbase[i]);
}
printf("\n");
}
//获取队头元素
int get_top_seqque(Seqque_t *psq, Data_type_t *data)
{
if(is_empty_seqque(psq))
{
return -1;
}
if(NULL != data)
{
*data = psq->pbase[psq->head];
printf("top : %d\n", *data);
}
return 0;
}
//出队
int pop_seqque(Seqque_t *psq, Data_type_t *data)
{
if(is_empty_seqque(psq))
{
return -1;
}
if(NULL != data)
{
*data = psq->pbase[psq->head];
}
psq->head = (psq->head + 1) % SEQQUE_MAX_LEN;
return 0;
}
//销毁
void destroy_seqque(Seqque_t **psq)
{
free((*psq)->pbase);
free(*psq);
*psq = NULL;
}
int main(void)
{
Seqque_t *psq = create_seqque();
if (NULL == psq)
{
return -1;
}
for (int i = 0; i < 10; i++)
{
push_seqque(psq, i);
}
seqque_for_each(psq);
Data_type_t data;
get_top_seqque(psq, &data);
pop_seqque(psq, &data);
pop_seqque(psq, &data);
seqque_for_each(psq);
push_seqque(psq, 9);
seqque_for_each(psq);
destroy_seqque(&psq);
return 0;
}
(3)队列与栈的区别
核心操作原则
栈 (Stack):
后进先出 (LIFO - Last In First Out)
像一摞盘子:最后放上去的盘子(栈顶),总是最先被取走。
操作只在一端(栈顶)进行:
push
(压栈/入栈)、pop
(弹栈/出栈)、peek/top
(查看栈顶)。队列 (Queue):
先进先出 (FIFO - First In First Out)
像排队:最早排队的人(队头),最先被服务离开。
操作在两端进行:
enqueue
(入队)在队尾,dequeue
(出队)在队头,peek/front
(查看队头)。操作位置
栈: 所有有效操作(增、删、查)都集中在同一端(栈顶)。
队列: 插入(
enqueue
)发生在队尾 (Rear),删除(dequeue
)发生在队头 (Front)。