如何复制*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的两次冗余拷贝和临时内存分配。
必须注意的细节
内存边界检查
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) }终止符必须拷贝
Go字符串本身不包含C风格的\0终止符,因此必须主动拷贝这个字节,否则C侧读取字符串时会出现未定义行为(比如读到垃圾数据)。Go字符串的安全性
Go字符串是不可变的,其底层内存不会被Go运行时随意修改,因此直接取其指针进行拷贝是安全的。
内容的提问来源于stack exchange,提问作者無名前
相关产品推荐
相关产品推荐

