Linux sentinel写法

在linux驱动里我们经常能看到类似下面的写法:

static const struct of_device_id asensm6_of_match[] = {
	{ .compatible = DRIVER_COMPATIBLE },
	{ /* sentinel */ },
};

static const struct of_device_id rockchip_pinctrl_dt_match[] = {
	{ .compatible = "rockchip,px30-pinctrl",
		.data = &px30_pin_ctrl },
	{ .compatible = "rockchip,rv1108-pinctrl",
		.data = &rv1108_pin_ctrl },
	{ .compatible = "rockchip,rv1126-pinctrl",
		.data = &rv1126_pin_ctrl },
	{ .compatible = "rockchip,rk1808-pinctrl",
		.data = &rk1808_pin_ctrl },
	{ .compatible = "rockchip,rk2928-pinctrl",
		.data = &rk2928_pin_ctrl },
	{ .compatible = "rockchip,rk3036-pinctrl",
		.data = &rk3036_pin_ctrl },
	{ .compatible = "rockchip,rk3066a-pinctrl",
		.data = &rk3066a_pin_ctrl },
	{ .compatible = "rockchip,rk3066b-pinctrl",
		.data = &rk3066b_pin_ctrl },
	{ .compatible = "rockchip,rk3128-pinctrl",
		.data = (void *)&rk3128_pin_ctrl },
	{ .compatible = "rockchip,rk3188-pinctrl",
		.data = &rk3188_pin_ctrl },
	{ .compatible = "rockchip,rk3228-pinctrl",
		.data = &rk3228_pin_ctrl },
	{ .compatible = "rockchip,rk3288-pinctrl",
		.data = &rk3288_pin_ctrl },
	{ .compatible = "rockchip,rk3308-pinctrl",
		.data = &rk3308_pin_ctrl },
	{ .compatible = "rockchip,rk3328-pinctrl",
		.data = &rk3328_pin_ctrl },
	{ .compatible = "rockchip,rk3368-pinctrl",
		.data = &rk3368_pin_ctrl },
	{ .compatible = "rockchip,rk3399-pinctrl",
		.data = &rk3399_pin_ctrl },
	{},
};

可以看到数组声明初始化时的尾部会有一个空白的{}, 对于GCC编译器来说它会被初始化为0,也就是说{}等价于{.a = 0, .b = 0}, linux的用意在于防止数组越界以及不需要去动态计算大小。
例如我们现在想遍历一个数组,那么终止条件就是判断当前是否为结束符,如果是结束符则遍历结束。
在声明中使用哨兵主要目的简化链表数组的算法步骤,使得判断终止变得比较单一了,无论什么条件只要遇到了哨兵那就意味着这个数组的遍历该结束了,因为判断比较单一这样能够提升CPU分支预测命中率,因为如果判断语句较为复杂会导致CPU分支预测失败率提高,当分支预测失败时CPU为了保证程序能够正常运行下去会重新回滚去执行这就间接导致了这个代码被放入热路径的概率会非常小。
此外在遍历数组时不需要去关心它的大小了,也不需要去计算,只需要简单的判断是否指向了哨兵元素就可以了,因为它判断的单一性,会让CPU很容易预测并将代码放入热路径(CPU缓存)里加快寻址与执行过程让循环代码变得更紧凑

<think>嗯,用户现在在问C语言实现双向环形队列的简洁代码及解释。我之前已经回答过关于双向环形链表的实现,但现在用户提到的是“双向环形队列”,可能需要注意这两个数据结构的区别。首先,我需要确认用户是否真的需要队列的实现,还是误将链表说成队列。 根据之前的对话历史,用户第一次问的是双向环形链表,而这次提到了队列。队列通常是先进先出(FIFO)的结构,而链表更灵活。双向环形队列可能结合了队列的操作方式和环形双向链表的结构。但严格来说,队列和链表是不同的数据结构,但环形双向链表可以用来实现队列,因为环形结构允许在两端高效地进行插入和删除操作,这正是双端队列(Deque)的特性。 接下来需要参考用户提供的引用内容。引用[1]是关于双向循环链表的C语言实现代码,这与之前的回答一致。引用[2]提到了斐波那契堆的实现,可能与当前问题无关。引用[3]解释了链表的结构,提到指针的使用。这些引用中,引用[1]最相关,但用户现在需要的是队列而非链表。 用户可能混淆了链表和队列,或者希望用双向环形链表来实现队列的功能。需要明确双向环形队列的具体需求:是否支持两端插入和删除?是否遵循FIFO规则?通常,双端队列(Deque)允许在头部和尾部进行插入和删除,而环形结构可以优化空间利用率,避免频繁的内存分配。 考虑到用户之前的问题是关于双向环形链表,而这次可能想用类似结构实现队列,我应该提供一个基于双向环形链表实现的双端队列,支持高效的入队和出队操作。同时,保持代码简洁,有详细解释,并区分队列与链表的差异。 需要确保代码结构正确,包括初始化、入队、出队、判空、销毁等操作。解释部分需要说明环形结构的优势,比如O(1)时间复杂度的操作,内存管理等。此外,要指出这种实现适用于需要高效前端和后端操作的场景,如缓存系统、任务调度等。 最后,用户可能还需要相关的问题,比如与普通队列的区别、性能优势、应用场景等,这些问题应该在回答后列出,以帮助用户进一步理解。</think>### C语言双向环形队列实现(基于双向链表) #### 数据结构定义 ```c #include <stdlib.h> typedef struct DQueueNode { int data; struct DQueueNode *prev; struct DQueueNode *next; } DQueue; // 初始化空队列(创建哨兵节点) DQueue* init_dqueue() { DQueue *sentinel = (DQueue*)malloc(sizeof(DQueue)); sentinel->prev = sentinel->next = sentinel; // 自环结构[^1] return sentinel; } ``` #### 核心操作实现 ```c // 队尾插入(时间复杂度O(1)) void enqueue_rear(DQueue *q, int val) { DQueue *new_node = (DQueue*)malloc(sizeof(DQueue)); new_node->data = val; // 插入到哨兵节点前(保持环形) new_node->prev = q->prev; new_node->next = q; q->prev->next = new_node; q->prev = new_node; } // 队头删除(时间复杂度O(1)) int dequeue_front(DQueue *q) { if (q->next == q) return -1; // 空队列检测 DQueue *target = q->next; int val = target->data; // 跳过被删除节点 q->next = target->next; target->next->prev = q; free(target); return val; } // 检查队列是否为空 int is_empty(DQueue *q) { return q->next == q; // 哨兵节点自环即空[^1] } // 销毁队列 void destroy_dqueue(DQueue *q) { DQueue *cur = q->next; while (cur != q) { // 环形终止条件 DQueue *temp = cur; cur = cur->next; free(temp); } free(q); } ``` #### 代码特性说明 1. **哨兵节点技术**:通过始终存在的哨兵节点统一处理边界条件,避免空指针异常[^1] 2. **环形内存结构**:所有节点组成闭合环,插入/删除只需修改相邻节点指针 3. **双端操作能力**:支持`enqueue_front/enqueue_rear`和`dequeue_front/dequeue_rear`(示例实现队尾插入和队头删除) 4. **高效内存利用**:节点动态分配,环形结构避免内存碎片 #### 使用示例 ```c int main() { DQueue *q = init_dqueue(); // 入队三个元素 enqueue_rear(q, 10); enqueue_rear(q, 20); enqueue_rear(q, 30); // 出队两个元素 printf("%d ", dequeue_front(q)); // 10 printf("%d ", dequeue_front(q)); // 20 destroy_dqueue(q); return 0; } ``` #### 应用场景 1. 网络数据包缓冲区(支持高效的双端操作) 2. 浏览器前进/后退栈实现 3. 实时系统事件队列(Linux内核kfifo实现类似结构)[^1] 4. 多线程工作窃取队列(Work-Stealing Queue) --- ### 关键特性对比 | 特性 | 普通队列 | 双向环形队列 | |-------------------|--------------|---------------------| | 插入位置 | 仅队尾 | 队头/队尾均可 | | 删除位置 | 仅队头 | 队头/队尾均可 | | 时间复杂度 | O(1) | O(1) | | 内存结构 | 线性 | 环形 | | 空队列检测 | front==NULL | sentinel->next==self| --- ### 相关问题 1. 如何实现双向环形队列的线程安全版本? 2. 环形队列相比数组实现的循环队列有哪些优势? 3. 这种队列结构在哪些场景下可能产生内存泄漏? 4. 如何扩展此队列使其支持泛型数据类型?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

17岁boy想当攻城狮

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值