概述

在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

--完--