You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 07:05:09