🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 缘由 现在有个应用场景,我们要在`go`中获取`C`结构体数组中的值。在`C`语言中,对于结构体数组,我们可以利用指针的偏移量来获取我们想要的值的位置。在`go`中我们怎么使用`C`指针和偏移量进行运算呢?下面的文字中`C`指的是`C语言` ## 利用cgo作为桥梁,遍历C结构体数组 `unsafe.Pointer`在C语言中类似`void*`,但是在go中无法用unsafe.Pointer进行运算。 不过`go`中有一种类型`uintptr`可以与`unsafe.Pointer`进行互相转换,并且可以进行运算。于是我有如下思路: * 利用`unsafe.Pointer`将C指针转化为go指针,也就是获取结构体数组的地址 * 然后转成`uintptr` * 计算出元素偏移量,根据元素偏移量取出元素 ## 举例 声明一个C数组 ```cpp #include <stdio.h> #include <stdlib.h> typedef struct { char *name; int age; }person; //new一个长度为n的person结构体数组 person* NewPersonArray(int n) { person *pon; pon =(person*)malloc(sizeof(person)*n); //初始化name for(int i = 0; i<n;i++){ pon[i].name = (char*)malloc(sizeof(char)*10); pon[i].age = i; sprintf(pon[i].name, "name:%d", i); } return pon; } //释放内存 void freePersonArray(person* pon, int len) { for (int i = 0; i < len; i ++){ free(pon[i].name); } free(pon); } ``` C已经做好了内存分配和内存释放,接下来看看在go中 怎么使用C的数据 ```go func main() { var person *C.person n := 10 person = C.NewPersonArray(C.int(n)) //new一个长度为n的person数组 //用go的方式遍历C数组 for i:=0; i < n; i++ { p := *(*C.person)(unsafe.Pointer(uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i)))) fmt.Printf("i: %d, p.name: %s, p.age: %d\n", i, C.GoString(p.name), int(p.age) ) } C.freePersonArray(person, C.int(n)) } ``` 关键的一步是计算偏移量并进行运算: ```go p := *(*C.person)(unsafe.Pointer(uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i)))) ``` 我们来把这个步骤分解一下: 第一步,将C指针转化为`unsafe.Pointer`,然后转化为`uintptr` ```go uintptr(unsafe.Pointer(person)) ``` 第二步,计算每个结构体所占用的大小 `C.sizeof_person`,并乘以 `i`,然后转化为`uintptr`,得到偏移量, 这个`person`是结构体名字。 ```go uintptr(C.sizeof_person*C.int(i)) ``` 第三步,把第一步的`uintptr`和第二步得到的偏移量相加,得到一个新的`uintptr`值, ```go uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i)) ``` 第四步,把第三部得到的`uintptr`转化为`unsafe.Pointer`,然后再强制转化为`*C.person`,这就类似`C语言`中的指针运算:`指针+偏移量`。 ```go p := *(*C.person)(unsafe.Pointer(uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i)))) ``` ## 完整代码 通过上面的四个步骤就可以利用`go`语言从`C`结构体数组中取出元素,完整代码如下: ```go package main /* #include <stdio.h> #include <stdlib.h> typedef struct { char *name; int age; }person; person* NewPersonArray(int n) { person *pon; pon =(person*)malloc(sizeof(person)*n); //初始化name for(int i = 0; i<n;i++){ pon[i].name = (char*)malloc(sizeof(char)*10); pon[i].age = i; sprintf(pon[i].name, "name:%d", i); } return pon; } void freePersonArray(person* pon, int len) { for (int i = 0; i < len; i ++){ free(pon[i].name); } free(pon); } */ import "C" import ( "fmt" "unsafe" ) func main() { var person *C.person n := 10 person = C.NewPersonArray(C.int(n)) //new一个长度为n的person数组 //用go的方式遍历C数组 for i:=0; i < n; i++ { p := *(*C.person)(unsafe.Pointer(uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i)))) fmt.Printf("i: %d, p.name: %s, p.age: %d\n", i, C.GoString(p.name), int(p.age) ) } C.freePersonArray(person, C.int(n)) } ``` 输出 ``` i: 0, p.name: name:0, p.age: 0 i: 1, p.name: name:1, p.age: 1 i: 2, p.name: name:2, p.age: 2 i: 3, p.name: name:3, p.age: 3 i: 4, p.name: name:4, p.age: 4 i: 5, p.name: name:5, p.age: 5 i: 6, p.name: name:6, p.age: 6 i: 7, p.name: name:7, p.age: 7 i: 8, p.name: name:8, p.age: 8 i: 9, p.name: name:9, p.age: 9 ```