go 中指针的特殊使用场景记录

一 slice 作为函数参数

在go 中,slice 结构体如下:

type slice struct {
	// uintptr 是一种特殊的无符号整型,实际占一个机器字大小(32位系统占有4个字		 
	//节,64位系统占有8个字节),而指针类型*T同样占一个机器字大小,所以uintptr
	// 足以存储一个指针
	Data uintptr
	Len int
	Cap int
}

在下文示例中,go中如果让params作为函数参数,那么相当于拷贝生成了一个slice结构体params2,结构体params2的Data与原来的结构体params中的Data指向内存中相同的位置。但是如果在Test方法中对params 进行扩容,新的结构体params2指向的内存空间位置必然会发生改变,但原来的结构体params指向的内存空间则不会发生改变。

func Test( params []int) {
}

如果希望将切片的变化从方法中传出,则可以传入一个结构体指针,如下所示:

func Test( params  *[]int){
}

测试如下:

func main() {
	m := make([]int, 0, 2)
	m = append(m, 0)
	m = append(m, 1)
	Test(m)
	fmt.Println(m)
	Test2(&m)
	fmt.Println(m)	
}
func Test( params []int){
	params = append(params, 2)
}
func Test2(params *[]int){
	*params = append(*params, 3)
}
// 输出:
//[0 1]
//[0 1 3]
二 []byte 与 string 的转换

在项目代码中,鉴权过程需要对字符串类型转换为byte数组,常规方式如下所示,在需要频繁进行转换的场景下,会影响程序性能:

var a = []byte("hello boy")
var b = string(a)

后来参考如下的处理方式,
transfer

从类型type上看,string相当于 [2]uintptr , [ ]byte相当于 [3]uintptr ,因此直接进行转换操作。

func str2bytes(s string) []byte {
      x := (*[2]uintptr)(unsafe.Pointer(&s))  
      // 类型转换, *string -> &[2]uintptr
     h := [3]uintptr{x[0], x[1], x[1]}  
     // 生成新的类型描述 , [3]uintptr
    return *(*[]byte)(unsafe.Pointer(&h)) 
     // 类型转换,&[3]uintptr -> *[ ]byte ,然后取值。
 }
 func bytes2str(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}

后来发现在go源码库中提供了更清晰的实现方式:

// 源码库中reflect包
type SliceHeader struct {
	Data uintptr
	Len int
	Cap int
}
type StringHeader struct {
	Data unsafe.Pointer
	Len int
}
// github.com/cespare/xxhash/xxhash_unsafe.go
func Sum64String(s string) uint64 {
	var b []byte
	bh := (*SliceHeader)(unsafe.Pointer(&b))
	bh.Data = (*StringHeader)(unsafe.Pointer(&s)).Data
	bh.Len = len(s)
	bh.Cap = len(s)
	return Sum64(bh)
}
三 结构体的拷贝

以go 源码中 request.go 中WithContext 方法作为示例:
这个方法是一个Request的浅拷贝实现,
考虑到新Request中的URL 是指针类型,因此需要进行深复制.

// net/http/request.go
tyep Request struct{
	Method string
	 URL *url.URL
	 Header Header
	 Body io.ReadCloser
	 ctx context.Context
	 ...
}
func (r *Request) withContext(ctx context.Context) *Request {
	if ctx == nil { panic{" nil context") }
	r2 := new(Request)
	*r2 = *r
	r2.Ctx = ctx
	// Deep copy the URL because it is not a map, and the url is
	// mutable by users WithContext 
	if r.URL != nil {
		r2Url := new(url.URL)
		*r2Url = *r.URL
		r2.URL = r2Url
	}
	return r2
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值