You need to enable JavaScript to run this app. C语言如何让函数支持传入任意包含alive字段的结构体类型
优惠活动
大模型
产品
解决方案
定价
更多

C语言如何让函数支持传入任意包含alive字段的结构体类型

实现通用链表清理函数的两种方案

C 语言本身没有内置泛型语法,要让清理函数支持任意包含 alive 字段和 next 指针的链表结构体,可以用以下两种成熟方案:


方案1:宏实现(最简便,无需额外传参)

宏会在编译期针对不同结构体类型展开,自动匹配成员,只要你的链表结构体符合「包含next同类型指针、bool类型alive字段」的约定,就可以直接使用,还支持默认省略free_everything参数(默认值为false):

#include <stdbool.h>
#include <stdlib.h>

// 处理默认参数:如果只传2个参数,free_everything默认是false
#define CLEAN_LIST(...)  CLEAN_LIST_IMPL(__VA_ARGS__, false)
#define CLEAN_LIST_IMPL(head, nr_ptr, free_everything)  do { \
    typeof(head) prev = head; \
    typeof(head) curr = head; \
    typeof(head) nxt = head->next; \
    int itr = *(nr_ptr); \
    for(int i=0; i<itr; i++) { \
        curr = nxt; \
        if(prev->next != nxt) \
            prev = prev->next; \
        if(i != itr-1) \
            nxt = nxt->next; \
        if(!(curr->alive) || free_everything) { \
            if(i != itr-1) \
                prev->next = nxt; \
            else \
                prev->next = NULL; \
            free(curr); \
            *(nr_ptr) -= 1; \
        } \
    } \
} while(0)

使用示例:

// 自定义的任意链表结构体都能用
struct my_node {
    int data;
    bool alive;
    struct my_node* next;
};
// 使用时
struct my_node* head = /* 哨兵头结点 */;
int nr = 10;
// 仅清理alive为false的节点(省略第三个参数)
CLEAN_LIST(head, &nr);
// 清理所有节点
CLEAN_LIST(head, &nr, true);

方案2:通用函数实现(用偏移量操作成员)

如果不想用宏,可以通过stddef.h的offsetof宏传入next和alive字段在结构体中的偏移量,实现纯函数的泛型支持:

#include <stdbool.h>
#include <stdlib.h>
#include <stddef.h>

void clean_list(void* head, int* nr_elements_ptr, bool free_everything, 
                size_t next_offset, size_t alive_offset) {
    // 用char*做指针运算,单位是1字节
    char* prev = head;
    char* curr = head;
    // 取head的next指针
    char* nxt = *(char**)(prev + next_offset);
    int itr = *nr_elements_ptr;
    
    for(int i=0; i<itr; i++) {
        curr = nxt;
        if(*(char**)(prev + next_offset) != nxt)
            prev = *(char**)(prev + next_offset);
        if(i != itr-1)
            nxt = *(char**)(nxt + next_offset);
        // 取curr的alive字段
        bool alive = *(bool*)(curr + alive_offset);
        if(!alive || free_everything) {
            if(i != itr-1)
                *(char**)(prev + next_offset) = nxt;
            else
                *(char**)(prev + next_offset) = NULL;
            free(curr);
            *nr_elements_ptr -=1;
        }
    }
}

使用示例:

struct my_node {
    int data;
    bool alive;
    struct my_node* next;
};
struct my_node* head = /* 哨兵头结点 */;
int nr =10;
// 传入两个字段的偏移量即可
clean_list(head, &nr, false, offsetof(struct my_node, next), offsetof(struct my_node, alive));

注意

两种方案都和你原有实现逻辑一致,默认链表使用哨兵头结点,如果你的链表是无哨兵的头结点直接存数据的结构,调整开头的指针初始化逻辑即可。

内容的提问来源于stack exchange,提问作者JA3G3R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 15:06:08