You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

C语言实现适配disk_inode与cache的通用链表(无标准库依赖)

通用链表适配disk_inode_t与cache结构体的实现方案(C语言,无标准库)

当然可以搞定!在没法用标准C库的情况下,咱们可以用C语言里模拟泛型链表的经典思路——在目标结构体里嵌入通用链表节点,这样就能让同一个链表框架适配disk_inode_tcache这两个完全不同的结构体。

第一步:定义通用链表节点

先搞一个最基础的链表节点结构,只负责链表的连接逻辑:

// 通用链表节点,所有要加入链表的结构体都需要嵌入这个成员
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

火山引擎 最新活动