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

cgo如何编写包装器释放返回给Python的unsafe.Pointer内存

问题根源

  1. Go侧导出的FreeCByte参数类型错误,接收的是指针的指针而非实际分配的内存指针,导致释放的是错误的内存地址
  2. Python侧未正确声明FreeCByte的参数类型,调用时类型不匹配
  3. 释放指针后仍然访问指针指向的内存,存在野指针访问风险

修正方案

第一步:修正Go侧释放逻辑

直接接收unsafe.Pointer类型的内存地址,传入C.free即可:

// #include <stdlib.h>
import "C"
import (
    "encoding/binary"
    "unsafe"
)

//export clientpy
func clientpy(url *C.char, headersfrompy *C.char, proxy *C.char) unsafe.Pointer {
    s := C.GoString(url)
    headers := C.GoString(headersfrompy)
    p := C.GoString(proxy)

    request := UrlGet(s, headers, p)

    length := make([]byte, 8)
    binary.LittleEndian.PutUint64(length, uint64(len(request)))
    return C.CBytes(append(length, request...))
}

//export FreeCByte
func FreeCByte(b unsafe.Pointer) {
    C.free(b)
}

第二步:修正Python侧调用逻辑

  • 正确声明FreeCByte的参数类型
  • 将内存访问操作移到释放指针之前
  • 修复可变默认参数的隐藏bug
from ctypes import cdll
import ctypes, cchardet, json
from bs4 import BeautifulSoup

lib = cdll.LoadLibrary("./test.so")
# 配置clientpy函数类型
lib.clientpy.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
lib.clientpy.restype = ctypes.c_void_p # 用void_p对应Go的unsafe.Pointer兼容性更好

# 配置FreeCByte函数类型
lib.FreeCByte.argtypes = [ctypes.c_void_p]
lib.FreeCByte.restype = None

def newrequest(path, lister=None, proxy=None):
    # 避免可变默认参数导致的状态残留问题
    if lister is None:
        lister = {}
    if proxy is None:
        proxy = []
    try:
        print(f"proxy: {proxy}")
        ptr = lib.clientpy(path.encode("utf-8"), str(lister).encode("utf-8"), str(proxy).encode("utf-8"))
        # 读取前8位的长度标识
        length_ptr = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ubyte * 8))
        length = int.from_bytes(length_ptr.contents, byteorder="little")
        # 读取完整返回数据
        data_ptr = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ubyte * (8 + length)))
        data = bytes(data_ptr.contents[8:])
        # 所有内存访问操作必须放在释放前执行
        print(f'bytes: {data}')
        # 释放C侧分配的内存
        lib.FreeCByte(ptr)
        return data
    except Exception as e:
        print(f"请求出错: {e}")
        return None

额外说明

你注释掉的free(ptr)也可以正常使用,只要确保调用的free和分配内存的malloc属于同一个C标准库实例即可,使用Go导出的释放函数可以避免跨库内存释放的兼容性问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 15:42:00