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
相关产品推荐
相关产品推荐

