Golang | 利用指针进行数据转换
概述
在Go语言中,Slice本质是什么呢?是一个reflect.SliceHeader结构体和这个结构体中Data字段所指向的内存。String本质是什么呢?是一个reflect.StringHeader结构体和这个结构体所指向的内存。
在Go语言中,指针的本质是什么呢?是unsafe.Pointer和uintptr。
当你清楚了它们的本质之后,你就可以随意的玩弄它们,嘿嘿嘿。
获得 Slice 和 String 的内存数据
有一个 CGO 接口要调用,需要你把一个字符串数据或者字节数组数据从Go这边传递到C那边.
查了各种教程和文档,它们都告诉你要用 C.GoString 或 C.GoBytes 来转换数据。
但是,当你调用这两个函数的时候,发生了什么事情呢?这时候Go复制了一份数据,然后再把新数据的地址传给C,因为Go不想冒任何风险。
你的C程序只是想一次性的用一下这些数据,也不得不做一次数据复制,这对于一个性能癖来说是多麽可怕的一个事实!
这时候我们就需要一个黑魔法,来做到不拷贝数据又能把指针地址传递给C。
// returns &s[0], which is not allowed in go
func stringPointer(s string) unsafe.Pointer {
p := (*reflect.StringHeader)(unsafe.Pointer(&s))
return unsafe.Pointer(p.Data)
}
// returns &b[0], which is not allowed in go
func bytePointer(b []byte) unsafe.Pointer {
p := (*reflect.SliceHeader)(unsafe.Pointer(&b))
return unsafe.Pointer(p.Data)
}
以上就是黑魔法第一式,我们先去到Go字符串的指针,它本质上是一个 *reflect.StringHeader ,但是Go告诉我们这是一个 *string ,我们告诉Go它同时也是一个 unsafe.Pointer ,Go说好吧它是,于是你得到了unsafe.Pointer,接着你就躲过了Go的监视,偷偷的把unsafe.Pointer转成了 *reflect.StringHeader 。
有了 *reflect.StringHeader ,你很快就取到了Data字段指向的内存地址,它就是Go保护着不想给你看到的隐秘所在,你把这个地址偷偷告诉给了C,于是C就愉快的偷看了Go的隐私。
把string转化为 *C.char
package main
/*
#include <stdio.h>
int test1(const char* s)
{
puts(s);
}
*/
import "C"
import (
"reflect"
"unsafe"
)
func stringPointer(s string)unsafe.Pointer {
p := (*reflect.StringHeader)(unsafe.Pointer(&s))
return unsafe.Pointer(p.Data)
}
func GetCString(s string)*C.char {
v := s + "\000"
p := stringPointer(v)
return (*C.char)(p)
}
func main() {
s := "ahello"
C.test1(GetCString(s))
}
思考为什么 v := s + “\000” 字符串后面要加 “\000”.我尝试了,如果不加转换会报错.如果加上 “\0” 则字符串打印出来结尾会多一个 \0
可以自己尝试一下
把 []byte 转成 string
普通的转换方法是
b := []byte("hello world")
c := string(b) // []byte => string
d := []byte(c) // string =>> []byte
使用指针 unsafe.Pointe 来转换
package labs28
import "testing"
import "unsafe"
func Test_ByteString(t *testing.T) {
var x = []byte("Hello World!")
var y = *(*string)(unsafe.Pointer(&x))
var z = string(x)
if y != z {
t.Fail()
}
}
func Benchmark_Normal(b *testing.B) {
var x = []byte("Hello World!")
for i := 0; i < b.N; i ++ {
_ = string(x)
}
}
func Benchmark_ByteString(b *testing.B) {
var x = []byte("Hello World!")
for i := 0; i < b.N; i ++ {
_ = *(*string)(unsafe.Pointer(&x))
}
}
参考:https://studygolang.com/articles/2909
--完--
- 原文作者: 留白
- 原文链接: https://zfunnily.github.io/2020/11/Golang-%E5%88%A9%E7%94%A8%E6%8C%87%E9%92%88%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E8%BD%AC%E6%8D%A2/
- 更新时间:2024-04-16 01:01:05
- 本文声明:转载请标记原文作者及链接