摘要
在Go和C内存共享时,可能会出现错误cgo argument has Go pointer to Go pointer。该问题出现的主要原因是在Go1.6.2版本之后,GO语言的rumtime检查机制对C和GO之间的指针传递行为进行了强制检查,不允许传递指向Go内存地址的指针。
正文
在Go1.6.2版本之后,Go的runtime加入了指针违规传递检测机制。该机制主要针对Go向C传递带有指向其他Go内存的地址,具体见文献检查机制。
package main
import "C"
import (
"fmt"
"runtime"
"unsafe"
)
func main() {
fmt.Printf("Allocate a memory through Golang coding.\n");
c:=make([]*C.int,3);
for i,_:=range c{
fmt.Println(i,c[i])
}
C.array((**C.int)(unsafe.Pointer(&c[0])),3);
fmt.Printf("Display the processing result.\n")
for i,_:=range c{
fmt.Println(i,c[i],*c[i]);
}
runtime.GC();
}
执行结果:
package main
import "C"
import (
"fmt"
"runtime"
"unsafe"
)
func main() {
fmt.Printf("Allocate a memory through Golang coding.\n");
c:=make([]*C.int,3);
for i,_:=range c{
c[i]=new(C.int)
fmt.Println(i,c[i])
}
C.array((**C.int)(unsafe.Pointer(&c[0])),3);
fmt.Printf("Display the processing result.\n")
for i,_:=range c{
fmt.Println(i,c[i],*c[i]);
}
runtime.GC();
}
执行结果:
- 情况2 Go不能向C传递带有初始化内存空间的结构体
package main
import "C"
import (
"fmt"
"unsafe"
)
func main() {
var S A
fmt.Printf("*****before being transfered to C!\n");
S.Number=new(C.int)
fmt.Println(S.Number)
C.transfer((*C.A)(unsafe.Pointer(&S)))
fmt.Printf("*****after being transfered to C!\n");
fmt.Println(*S.Number)
}
type A struct{
Number *C.int
}
执行结果
package main
import "C"
import "fmt"
func main() {
point:=new(C.int);
*point=4;
fmt.Println("******before being transfered to C Code!")
fmt.Printf("point=%p,*point=%d\n",point,*point)
C.transfer(&point);
fmt.Printf("point=%p,*point=%d\n",point,*point)
}
执行结果为
解决办法
解决办法主要有两类,临时性解决和永久性解决。临时性解决主要针对某次编译绕开检测,缺点是每次执行都要带参数GODEBUG=cgocheck=0这个标识;永久性解决则将该GODEBUG=cgocheck=0做成系统环境变量,使每次运行都自动调用该参数。
在编译开始时,加入GODEBUG=cgocheck=0,绕开rutime的编译检查机制,具体执行方法如下所示:
将GODEGUG=cgocheck=0,加入Linux系统的~/.bashrc文件的末尾,实现启动默认添加该变量。 执行结果为
总结
在CGO交互中,出现报错cgo argument has Go pointer to Go pointer情形的主要原因是,GO语言的rumtime检查机制对C和GO之间的指针传递行为进行了强制检查。绕开检查机制是目前已知的处理方式。其实,为了避免导致不必要的内存泄露,尽量不用上述的指针传递方式进行C语言和GO的内存共享。
|