Cgo | 遍历C结构体数组
缘由
现在有个应用场景,我们要在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数组
#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的数据
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))
}
关键的一步是计算偏移量并进行运算:
p := *(*C.person)(unsafe.Pointer(uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i))))
我们来把这个步骤分解一下:
第一步,将C指针转化为unsafe.Pointer
,然后转化为uintptr
uintptr(unsafe.Pointer(person))
第二步,计算每个结构体所占用的大小 C.sizeof_person
,并乘以 i
,然后转化为uintptr
,得到偏移量, 这个person
是结构体名字。
uintptr(C.sizeof_person*C.int(i))
第三步,把第一步的uintptr
和第二步得到的偏移量相加,得到一个新的uintptr
值,
uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i))
第四步,把第三部得到的uintptr
转化为unsafe.Pointer
,然后再强制转化为*C.person
,这就类似C语言
中的指针运算:指针+偏移量
。
p := *(*C.person)(unsafe.Pointer(uintptr(unsafe.Pointer(person)) + uintptr(C.sizeof_person*C.int(i))))
完整代码
通过上面的四个步骤就可以利用go
语言从C
结构体数组中取出元素,完整代码如下:
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
--完--
- 原文作者: 留白
- 原文链接: https://zfunnily.github.io/2021/01/cgostruct/
- 更新时间:2024-04-16 01:01:05
- 本文声明:转载请标记原文作者及链接