Golang实践:低级编程

发布于 2017-09-07 · 本文总共 4997 字 · 阅读大约需要 15 分钟

unsafe包广泛使用在和操作系统交互的低级包(如runtime、os、syscall和net)中,但是普通程序从来不使用它;

unsafe.Sizeof\Alignof\Offsetof

Sizeof: 参数在内存中占用的字节长度

Alignof: 参数类型所要求的对齐方式

Offsetof: 计算成员相对于结构体起始地址的偏移量

import (
	"fmt"
	"unsafe"
)

type Struct1 struct {
	c bool
	a float64
	b int16
}

type Struct2 struct {
	a float64
	b int16
	c bool
}

type Struct3 struct {
	c bool
	b int16
	a float64
}

var boolTest bool

var stringTest string

var mapTest map[int]int

var funcTest  func()

var chanTest chan int

var interfaceTest interface{}


func SizeOf()  {

	fmt.Println("Size of float64: ", unsafe.Sizeof(float64(0)))

	fmt.Println("Size of bool: ", unsafe.Sizeof(boolTest))

	fmt.Println("Size of string: ", unsafe.Sizeof(stringTest))

	fmt.Println("Size of map: ", unsafe.Sizeof(mapTest))

	fmt.Println("Size of func: ", unsafe.Sizeof(funcTest))

	fmt.Println("Size of channel: ", unsafe.Sizeof(chanTest))

	fmt.Println("Size of interface: ", unsafe.Sizeof(interfaceTest))

	s1 := Struct1{}
	fmt.Println("Size of struct1: ", unsafe.Sizeof(Struct1{}),
		"\n Alignof struct1: ", unsafe.Alignof(Struct1{}),
		"\n\n Alignof struct1.a: ", unsafe.Alignof(s1.a),
		"\n Alignof struct1.b: ", unsafe.Alignof(s1.b),
		"\n Alignof struct1.c: ", unsafe.Alignof(s1.c),
		"\n\n Offsetof struct1.a: ", unsafe.Offsetof(s1.a),
		"\n Offsetof struct1.b: ", unsafe.Offsetof(s1.b),
		"\n Offsetof struct1.c: ", unsafe.Offsetof(s1.c))

	fmt.Println("Size of struct2: ", unsafe.Sizeof(Struct2{}),
		"\n Alignof struct1: ", unsafe.Alignof(Struct1{}),
		"\n\n Alignof struct1.a: ", unsafe.Alignof(s1.a),
		"\n Alignof struct1.b: ", unsafe.Alignof(s1.b),
		"\n Alignof struct1.c: ", unsafe.Alignof(s1.c),
		"\n\n Offsetof struct1.a: ", unsafe.Offsetof(s1.a),
		"\n Offsetof struct1.b: ", unsafe.Offsetof(s1.b),
		"\n Offsetof struct1.c: ", unsafe.Offsetof(s1.c))

	fmt.Println("Size of struct3: ", unsafe.Sizeof(Struct3{}),
		"\n Alignof struct1: ", unsafe.Alignof(Struct1{}),
		"\n\n Alignof struct1.a: ", unsafe.Alignof(s1.a),
		"\n Alignof struct1.b: ", unsafe.Alignof(s1.b),
		"\n Alignof struct1.c: ", unsafe.Alignof(s1.c),
		"\n\n Offsetof struct1.a: ", unsafe.Offsetof(s1.a),
		"\n Offsetof struct1.b: ", unsafe.Offsetof(s1.b),
		"\n Offsetof struct1.c: ", unsafe.Offsetof(s1.c))
}

Size of float64: 8 Size of bool: 1 Size of string: 16 Size of map: 8 Size of func: 8 Size of channel: 8 Size of interface: 16 Size of struct1: 24 Alignof struct1: 8

Alignof struct1.a: 8 Alignof struct1.b: 2 Alignof struct1.c: 1

Offsetof struct1.a: 8 Offsetof struct1.b: 16 Offsetof struct1.c: 0 Size of struct2: 16 Alignof struct1: 8

Alignof struct1.a: 8 Alignof struct1.b: 2 Alignof struct1.c: 1

Offsetof struct1.a: 8 Offsetof struct1.b: 16 Offsetof struct1.c: 0 Size of struct3: 16 Alignof struct1: 8

Alignof struct1.a: 8 Alignof struct1.b: 2 Alignof struct1.c: 1

Offsetof struct1.a: 8 Offsetof struct1.b: 16 Offsetof struct1.c: 0

unsafe.Pointer

如何零拷贝将string转换为[]byte? 或者将[]byte转换为string?

string和slice的底层数据结构:

// StringHeader is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type StringHeader struct {
	Data uintptr
	Len  int
}

// stringHeader is a safe version of StringHeader used within this package.
type stringHeader struct {
	Data unsafe.Pointer
	Len  int
}

// SliceHeader is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

// sliceHeader is a safe version of SliceHeader used within this package.
type sliceHeader struct {
	Data unsafe.Pointer
	Len  int
	Cap  int
}

代码实现和测试:

import (
	"reflect"
	"unsafe"
)

func StringToBytes(s string) []byte {

	stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s))
	sliceHeader := reflect.SliceHeader{
		Data:stringHeader.Data,
		Len:stringHeader.Len,
		Cap:stringHeader.Len,
	}

	return *(*[]byte)(unsafe.Pointer(&sliceHeader))
}

func BytesToString(b []byte)string  {
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	stringHeader := reflect.StringHeader{
		Data:sliceHeader.Data,
		Len:sliceHeader.Len,
	}

	return *(*string)(unsafe.Pointer(&stringHeader))

}

func TestBytesToString(t *testing.T) {
	s := "abcdefg"
	res := StringToBytes(s)
	stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s))

	fmt.Printf("%+v, %+v\n", &s, &stringHeader.Data)
	fmt.Println(res)

	bytes := []byte(s)
	res1 := BytesToString(bytes)
	fmt.Println(res1)
}

output:

0xc00005c350, 0xc00005c350 [97 98 99 100 101 102 103] abcdefg

reflect.DeepEqual

reflect.DeepEqual用来判断两个变量的值是否“深度”相等;

对基本类型使用内置的==操作符进行比较;

对组合类型,它逐层深入比较相应的元素;

  • 注意:
    值为nil的map和空map被判断为不相等; 值为nil的slice和空slice判断为不相等;
func IsEqual()  {

	var map1, map2 map[string]int = nil, make(map[string]int)
	res := reflect.DeepEqual(map1, map2)
	fmt.Println(res)

	var slice1, slice2 []int = nil, []int{}
	res1 := reflect.DeepEqual(slice1, slice2)
	fmt.Println(res1)
}

false false

使用cgo调用C代码

一个Go程序或许需要调用用C实现的硬件驱动程序;查询一个用C++实现的嵌入式数据库;或者使用一些以Fortan实现的线性代数协程;

import “C”

关于安全的注意事项

大多数程序员永远都不需要使用unsafe包; 当然,偶尔还是存在这种情况,其中一些关键代码最好还是通过unsafe来写; 如果在仔细研究和评估后确认unsafe包是最佳的选择,那么还是尽可能的限制在小范围内使用;

go版本

go version go1.13.6 darwin/amd64

refs

《The Go Programming Language》Alan A.A, Donovan & Brian W.Kernighan




本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ,转载请务必注明出处,谢谢。
声明:
本博客欢迎转发,但请保留原作者信息!
博客地址:邱文奇(qiuwenqi)的博客;
内容系本人学习、研究和总结,如有雷同,实属荣幸!
阅读次数:

文章评论

comments powered by Disqus


章节列表