C语言实现适配disk_inode与cache的通用链表(无标准库依赖)
通用链表适配disk_inode_t与cache结构体的实现方案(C语言,无标准库)
当然可以搞定!在没法用标准C库的情况下,咱们可以用C语言里模拟泛型链表的经典思路——在目标结构体里嵌入通用链表节点,这样就能让同一个链表框架适配disk_inode_t和cache这两个完全不同的结构体。
第一步:定义通用链表节点
先搞一个最基础的链表节点结构,只负责链表的连接逻辑:
// 通用链表节点,所有要加入链表的结构体都需要嵌入这个成员 typedef struct list_node { struct list_node *next; } list_node_t;
第二步:修改原有结构体,嵌入链表节点
把上面的list_node_t作为成员加到你的两个结构体里,位置随便放(只要是结构体的成员就行):
// 修改后的disk_inode_t,嵌入链表节点 typedef struct disk_inode { list_node_t node; // 通用链表节点 short type; /* file type */ short nlinks; /* number of directory entries referring to this file */ int size; /* file size in bytes */ short inode_indir_idx; /* pointers to the first NDIRECT blocks */ blknum_t direct[INODE_NDIRECT]; blknum_t indirect; /* The rest of the blocks */ } disk_inode_t; // 修改后的cache,嵌入链表节点 struct cache { list_node_t node; // 通用链表节点 short blocknr; char block[512]; };
第三步:实现通用链表操作函数
这些函数只操作list_node_t指针,完全不关心背后的具体结构体类型,真正做到通用:
// 在链表头部添加新节点 void list_add_head(list_node_t **head, list_node_t *new_node) { new_node->next = *head; *head = new_node; } // 从链表中删除指定节点(prev是待删节点的前一个节点,若为NULL则待删节点是头节点) void list_remove_node(list_node_t **head, list_node_t *prev, list_node_t *target) { if (prev == NULL) { *head = target->next; } else { prev->next = target->next; } target->next = NULL; // 清空指针,避免野指针 } // 遍历链表,传入回调函数处理每个节点 void list_traverse(list_node_t *head, void (*handler)(list_node_t *)) { list_node_t *current = head; while (current != NULL) { handler(current); current = current->next; } }
第四步:从链表节点还原到原结构体
要操作原结构体的字段,我们需要从list_node_t指针反推回父结构体的指针。这里可以自己实现一个container_of宏(不用依赖标准库的offsetof):
// 自定义宏:从链表节点指针获取父结构体指针 // 参数说明:ptr=链表节点指针,type=父结构体类型,member=父结构体中链表节点的成员名 #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
举个实际使用的例子,比如处理缓存节点和inode节点的回调函数:
// 处理cache节点的回调函数 void handle_cache_entry(list_node_t *node) { struct cache *cache_entry = container_of(node, struct cache, node); // 这里就可以操作cache_entry的字段了,比如: // cache_entry->blocknr = 100; // memcpy(cache_entry->block, some_data, 512); } // 处理disk_inode节点的回调函数 void handle_inode_entry(list_node_t *node) { disk_inode_t *inode = container_of(node, disk_inode_t, node); // 操作inode的字段,比如: // inode->size = 1024; // inode->nlinks++; }
关键注意事项
- 确保每个需要加入链表的结构体都正确嵌入了
list_node_t成员,成员名可以自定义(只要和container_of宏里的参数对应上就行) container_of宏的原理是计算链表节点在父结构体中的偏移量,用节点指针减去偏移量得到父结构体的起始地址,只要参数正确,这个操作是完全安全的- 所有链表操作都只依赖
list_node_t,不需要修改就能适配任意嵌入了该节点的结构体,真正实现了通用
内容的提问来源于stack exchange,提问作者user9237024




