mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 04:22:22 +00:00
ch2-2: 完善指针转化的内容
This commit is contained in:
parent
946ffe1c97
commit
135888686c
@ -356,17 +356,23 @@ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
|||||||
|
|
||||||
在C语言中可以通过`GoString`和`GoSlice`来访问Go语言的字符串和切片。如果是Go语言中数组类型,可以将数组转为切片后再行转换。如果字符串或切片对应的底层内存空间由Go语言的运行时管理,那么在C语言中不能长时间保存Go内存对象。
|
在C语言中可以通过`GoString`和`GoSlice`来访问Go语言的字符串和切片。如果是Go语言中数组类型,可以将数组转为切片后再行转换。如果字符串或切片对应的底层内存空间由Go语言的运行时管理,那么在C语言中不能长时间保存Go内存对象。
|
||||||
|
|
||||||
|
<!--
|
||||||
```go
|
```go
|
||||||
// 例子
|
// 例子
|
||||||
// 字符串和切片
|
// 字符串和切片
|
||||||
// 作为参数或返回值 和 C 函数通信
|
// 作为参数或返回值 和 C 函数通信
|
||||||
```
|
```
|
||||||
|
-->
|
||||||
|
|
||||||
关于CGO内存模型的细节在稍后章节中会详细讨论。
|
关于CGO内存模型的细节在稍后章节中会详细讨论。
|
||||||
|
|
||||||
## 指针和切片
|
## 指针间的转换
|
||||||
|
|
||||||
C语言和Go语言指针的转换可以看做是两种不同类型的指针之间的转换。在Go语言中我们无法在不同类型之间做转换,因此不同类型的指针之间也存在此限制。由于任意类型的指针均可和`unsafe.Pointer`相互转换,所以我们可以以`unsafe.Pointer`作为中间桥接类型实现不同类型指针之间的转换。
|
在C语言中,不同类型的指针是可以显式或隐式转换的,如果是隐式只是会在编译时给出一些警告信息。但是Go语言对于不同类型的转换非常严格,任何C语言中可能出现的警告信息在Go语言中都可能是错误!指针是C语言的灵魂,指针间的自由转换也是cgo代码中经常要解决的第一个重要的问题。
|
||||||
|
|
||||||
|
在Go语言中两个指针的类型完全一致则不需要转换可以直接通用。如果一个指针类型是用type命令在另一个指针类型基础之上构建的,换言之两个指针底层是相同完全结构的指针,那么我我们可以通过直接强制转换语法进行指针间的转换。但是cgo经常要面对的是2个完全不同类型的指针间的转换,原则上这种操作在纯Go语言代码是严格禁止的。
|
||||||
|
|
||||||
|
cgo存在的一个目的就是打破Go语言的禁制,恢复C语言应有的指针的自由转换和指针运算。以下代码演示了如何将X类型的指针转化为Y类型的指针:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var p *X
|
var p *X
|
||||||
@ -376,7 +382,31 @@ q = (*Y)(unsafe.Pointer(p)) // *X => *Y
|
|||||||
p = (*X)(unsafe.Pointer(q)) // *Y => *X
|
p = (*X)(unsafe.Pointer(q)) // *Y => *X
|
||||||
```
|
```
|
||||||
|
|
||||||
再结合`reflect.SliceHeader`类型,我们可以实现`[]X`和`[]Y`类型的切片转换:
|
为了实现X类型指针到Y类型指针的转换,我们需要借助`unsafe.Pointer`作为中间桥接类型实现不同类型指针之间的转换。`unsafe.Pointer`指针类型类似C语言中的`void*`类型的指针。
|
||||||
|
|
||||||
|
下面是指针间的转换流程的示意图:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
任何类型的指针都可以通过强制转换为`unsafe.Pointer`指针类型去掉原有的类型信息,然后再重新赋予新的指针类型而达到指针间的转换的目的。
|
||||||
|
|
||||||
|
## 数值和指针的转换
|
||||||
|
|
||||||
|
不同类型指针间的转换看似复杂,但是在cgo中已经算是比较简单的了。在C语言中经常遇到用普通数值表示指针的场景,也就是说如何实现数值和指针的转换也是cgo需要面对的一个问题。
|
||||||
|
|
||||||
|
为了严格控制指针的使用,Go语言禁止将数值类型直接转为指针类型!不过,Go语言针对`unsafe.Pointr`指针类型特别定义了一个uintptr类型。我们可以uintptr为中介,实现数值类型到`unsafe.Pointr`指针类型到转换。再结合前面提到的方法,就可以实现数值和指针的转换了。
|
||||||
|
|
||||||
|
下面流程图演示了如何实现int32类型到C语言的`char*`字符串指针类型的相互转换:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
转换分为几个阶段,在每个阶段实现一个小目标:首先是int32到uintptr类型,然后是uintptr到`unsafe.Pointr`指针类型,最后是`unsafe.Pointr`指针类型到`*C.char`类型。
|
||||||
|
|
||||||
|
## 切片间的转换
|
||||||
|
|
||||||
|
在C语言中数组也一种指针,因此两个不同类型数组之间到转换和指针间转换基本类似。但是在Go语言中,数组或数组对应到切片都不再是指针类型,因为我们也就无法直接实现不同类型到切片之间的转换。
|
||||||
|
|
||||||
|
不过Go语言的reflect包提供了切片类型到底层结构,再结合前面讨论到不同类型之间到指针转换技术就可以实现`[]X`和`[]Y`类型的切片转换:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var p []X
|
var p []X
|
||||||
@ -390,6 +420,10 @@ pHdr.Len = qHdr.Len * unsafe.Sizeof(q[0]) / unsafe.Sizeof(p[0])
|
|||||||
pHdr.Cap = qHdr.Cap * unsafe.Sizeof(q[0]) / unsafe.Sizeof(p[0])
|
pHdr.Cap = qHdr.Cap * unsafe.Sizeof(q[0]) / unsafe.Sizeof(p[0])
|
||||||
```
|
```
|
||||||
|
|
||||||
如果X和Y类型的大小不同,需要重新设置Len和Cap属性。需要注意的是,如果X或Y是空类型,上述代码中可能导致除0错误,实际代码需要根据情况酌情处理。
|
不同切片类型之间转换到思路是先为构造一个空的目标切片,然后用原有的切片底层数据填充目标切片。如果X和Y类型的大小不同,需要重新设置Len和Cap属性。需要注意的是,如果X或Y是空类型,上述代码中可能导致除0错误,实际代码需要根据情况酌情处理。
|
||||||
|
|
||||||
|
下面演示了切片间的转换的具体流程:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
针对CGO中常用的功能,作者封装了 "github.com/chai2010/cgo" 包,提供基本的转换功能,具体的细节可以参考实现代码。
|
针对CGO中常用的功能,作者封装了 "github.com/chai2010/cgo" 包,提供基本的转换功能,具体的细节可以参考实现代码。
|
||||||
|
Loading…
x
Reference in New Issue
Block a user