C语言垃圾回收结构体中void**的合理使用及类型兼容问题咨询
解决C语言引用计数赋值的类型兼容问题与线程安全实现
一、类型不兼容警告的解决方案
你遇到的指针类型不兼容问题,根源在于C语言不允许直接将struct dom_node**(即DOMStrongRef(struct dom_node)*)传递给期望void**(即DOMStrongRef(DOMAny)*)的函数。由于C没有原生泛型,改用**宏实现dom_strong_assign**是无需强制类型转换的最优方案:
/* dom/dom.h */ typedef void DOMAny; #define DOMStrongRef(T) T* #define DOMWeakRef(T) T* #define dom_strong_assign(dest, src) do { \ if (*(dest) != NULL) \ dom_strong_release(*(dest)); \ *(dest) = dom_strong_add_ref((DOMAny*)(src)); \ } while(0)
为什么用宏而不是inline函数?
- 宏可以直接处理任意指针类型的
dest,无需类型转换,完美匹配你定义的DOMStrongRef(T)语义。 do-while(0)包裹确保宏在任何上下文(比如if语句后不加花括号)都能正确执行,避免语法错误。
使用方式和你期望的完全一致,不会再出现类型警告:
DOMStrongRef(struct dom_node) node = dom_strong_add_ref(existing_node_ref); dom_strong_assign(&node, other_node); // 自动释放旧引用,赋值新引用
二、线程安全的实现思路
要让引用计数操作线程安全,必须用原子操作替代普通的整数增减,避免多线程下的竞态条件。基于C11标准的<stdatomic.h>可以实现跨平台的线程安全:
1. 修改DOMHeader的引用计数为原子类型
#include <stdatomic.h> typedef struct DOMHeader_s { struct vtable *vtable; atomic_int_least32_t strong_refcnt; // 原子强引用计数 atomic_int_least32_t weak_refcnt; // 原子弱引用计数 } DOMHeader;
2. 原子化add_ref和release操作
static inline DOMStrongRef(DOMAny) dom_strong_add_ref(DOMAny *any) { if (any != NULL) { DOMHeader *hdr = (DOMHeader*)any; atomic_fetch_add(&hdr->strong_refcnt, 1); } return any; } static inline void dom_strong_release(DOMStrongRef(DOMAny) any) { if (any == NULL) return; DOMHeader *hdr = (DOMHeader*)any; // 原子减1后判断是否为0,memory_order_acq_rel确保内存可见性 if (atomic_fetch_sub(&hdr->strong_refcnt, 1) == 1) { dom__destroy(any); // 最后一个引用释放时销毁对象 } }
3. 内存序的选择
atomic_fetch_add用memory_order_relaxed即可:因为引用计数的增减不需要同步其他内存操作,只要计数本身正确。atomic_fetch_sub用memory_order_acq_rel:确保在销毁对象前,所有对该对象的写入操作都已完成,同时其他线程能看到引用计数的变化。
4. 弱引用的线程安全注意点
弱引用的add_ref和release同样要用原子操作,另外在弱引用升级为强引用时,需要先检查强引用计数是否大于0,再原子递增(避免对象在检查后被销毁):
static inline DOMStrongRef(DOMAny) dom_weak_upgrade(DOMWeakRef(DOMAny) weak) { if (weak == NULL) return NULL; DOMHeader *hdr = (DOMHeader*)weak; // 先加载当前强引用计数,非0再尝试递增 int_least32_t cnt = atomic_load(&hdr->strong_refcnt); while (cnt > 0) { if (atomic_compare_exchange_weak(&hdr->strong_refcnt, &cnt, cnt + 1)) { return weak; } // 比较失败说明计数已变,重新加载 cnt = atomic_load(&hdr->strong_refcnt); } return NULL; // 对象已被销毁,升级失败 }
内容的提问来源于stack exchange,提问作者rshadr
相关产品推荐
相关产品推荐

