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

如何复制*C.char?Go与C交互中*C.char的memcpy等价实现

直接将Go字符串拷贝到C预分配内存的最优方案

核心实现(无额外拷贝)

直接利用Go字符串的底层内存,通过unsafe包转换指针后调用C.memcpy,仅需一次拷贝即可完成:

import (
    "C"
    "unsafe"
)

var myGoStr = "blabla"

//export GetString
func GetString(text *C.char) {
    strLen := len(myGoStr)
    // 获取Go字符串底层字节的指针(包含后续手动添加的终止符位置)
    srcPtr := unsafe.Pointer((*[1 << 30]byte)(unsafe.Pointer(&myGoStr))[:strLen+1:strLen+1])
    // 拷贝字符串内容 + 终止符'\0'到C预分配内存
    C.memcpy(unsafe.Pointer(text), srcPtr, C.size_t(strLen+1))
}

或者更直观的字节切片写法:

//export GetString
func GetString(text *C.char) {
    goBytes := []byte(myGoStr)
    copyLen := len(goBytes) + 1 // +1 是为了拷贝终止符'\0'
    C.memcpy(unsafe.Pointer(text), unsafe.Pointer(&goBytes[0]), C.size_t(copyLen))
}

关键优势

仅执行一次内存拷贝,直接从Go字符串的底层存储复制到C提供的内存,彻底避免了C.CString+C.strcpy的两次冗余拷贝和临时内存分配。

必须注意的细节

  1. 内存边界检查
    C侧必须保证预分配的内存空间≥len(myGoStr)+1字节(+1是为了容纳C字符串必需的终止符\0)。如果无法确保内存充足,建议给函数增加内存长度参数,做安全截断:

    //export GetString
    func GetString(text *C.char, bufSize C.size_t) {
        strLen := len(myGoStr)
        requiredLen := C.size_t(strLen + 1)
        
        if requiredLen > bufSize {
            // 内存不足时,仅拷贝bufSize-1字节,手动添加终止符
            copyLen := bufSize - 1
            C.memcpy(unsafe.Pointer(text), unsafe.Pointer(&myGoStr), copyLen)
            // 确保字符串终止
            (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(text)) + uintptr(copyLen))) = C.char(0)
            return
        }
        
        C.memcpy(unsafe.Pointer(text), unsafe.Pointer(&myGoStr), requiredLen)
    }
    
  2. 终止符必须拷贝
    Go字符串本身不包含C风格的\0终止符,因此必须主动拷贝这个字节,否则C侧读取字符串时会出现未定义行为(比如读到垃圾数据)。

  3. Go字符串的安全性
    Go字符串是不可变的,其底层内存不会被Go运行时随意修改,因此直接取其指针进行拷贝是安全的。

内容的提问来源于stack exchange,提问作者無名前

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 18:58:10