C 语言中的通用指针类型(void *)
void * 是 C 语言中的通用指针类型,它可以指向任意类型的数据。在 FreeRTOS 的链表实现中,它被广泛用于实现类型无关的通用数据结构。
1. 为什么需要 void *?
在 FreeRTOS 中,同一个链表结构(List_t)需要能够挂载不同类型的对象,比如:
- 任务控制块(
TCB_t) - 软件定时器控制块
- 事件组等待节点
如果链表节点的 pvOwner 成员固定为某种具体类型(比如 TCB_t *),那么链表就只能挂载任务,缺乏通用性。而使用 void *,就可以存储任何类型的指针,实现一个链表结构服务于多种内核对象。
2. 在宏中的具体用法
(1)设置拥有者:listSET_LIST_ITEM_OWNER
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) \
((pxListItem)->pvOwner = (void*)(pxOwner))
pxOwner通常是一个具体类型的指针(如TCB_t *),但链表节点只保存void *类型。- 强制转换为
void *是为了消除编译器关于类型不匹配的警告,明确表示“我们将这个指针视为通用指针存储起来”。
(2)获取拥有者:listGET_LIST_ITEM_OWNER
#define listGET_LIST_ITEM_OWNER( pxListItem ) \
( ( pxListItem )->pvOwner )
- 返回的是
void *,使用时需要由调用者强制转换回原本的类型,例如:TCB_t *pxTCB = (TCB_t *) listGET_LIST_ITEM_OWNER( pxListItem ); - 这种显式转换让代码的意图清晰,同时保持链表模块的通用性。
(3)比较指针是否指向哨兵节点:listGET_OWNER_OF_NEXT_ENTRY 中的 if 语句
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )
pxIndex是ListItem_t *类型,指向链表节点。&(pxConstList->xListEnd)是ListItem_t *类型,指向哨兵节点。- 将两者都转换为
void *再比较,是为了只比较地址值,而不关心它们的原始类型。这样可以避免不同指针类型之间的直接比较产生的编译警告或错误。
3. void * 的隐式与显式转换
- 任何类型的指针都可以隐式转换为
void *(C 语言标准允许)。 - 从
void *转换回具体类型指针时,需要显式强制转换,否则编译器会报警告或错误。
在 FreeRTOS 中,经常可以看到这样的模式:
// 存储
listSET_LIST_ITEM_OWNER( &xListItem, pxTCB ); // 隐式转换
// 取出
TCB_t *pxTCB = (TCB_t *) listGET_LIST_ITEM_OWNER( &xListItem );
4. 为什么在比较时也要强制转换?
在 listGET_OWNER_OF_NEXT_ENTRY 宏中,pxConstList->pxIndex 与 &(pxConstList->xListEnd) 的类型都是 ListItem_t *,按理说直接比较也是合法的。但 FreeRTOS 仍然使用了 (void *) 强制转换,主要原因有两点:
-
防止因类型限定符(如
const)导致的编译警告
在某些编译环境中,如果其中一个指针带有const限定而另一个没有,直接比较可能会产生警告。转换为void *后忽略掉限定符,让比较纯粹是地址比较。 -
代码风格的一致性
FreeRTOS 在涉及指针比较的地方习惯使用(void *)转换,明确表示“这里只比较地址”,提高了可移植性。
总结
void * 是 FreeRTOS 实现通用链表的关键手段。它允许链表节点持有任意类型的对象指针,使内核代码能够复用同一套链表操作宏来管理任务、定时器、事件等不同内核对象。在宏中使用 void * 进行强制转换,不仅消除了类型相关的编译警告,也清晰地表达了“我们只关心地址或通用指针”的设计意图。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)