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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go-Request 是 HTTP 客户端请求包,灵感来源于 Python Requests。 安装 go get -u github.com/mozillazg/request 文档 API 文档:https://godoc.org/github.com/mozillazg/request 使用 import (     "github.com/mozillazg/request" ) GET: c := new(http.Client) req := request.NewRequest(c) resp, err := req.Get("http://httpbin.org/get") j, err := resp.Json() defer resp.Body.Close()  // Don't forget close the response body POST: req.Data = map[string]string{     "key": "value",     "a":   "123", } resp, err := req.Post("http://httpbin.org/post") Cookies: req.Cookies = map[string]string{     "key": "value",     "a":   "123", } resp, err := req.Get("http://httpbin.org/cookies") Headers: req.Headers = map[string]string{     "Accept-Encoding": "gzip,deflate,sdch",     "Accept": "text/html,application/xhtml xml,application/xml;q=0.9,image/webp,*/*;q=0.8", } resp, err := req.Get("http://httpbin.org/get") Files: f, err := os.Open("test.txt") req.Files = []request.FileField{     request.FileField{"file", "test.txt", f}, }resp, err := req.Post("http://httpbin.org/post") Json: req.Json = map[string]string{    "a": "A",    "b": "B", }resp, err := req.Post("http://httpbin.org/post") req.Json = []int{1, 2, 3} resp, err = req.Post("http://httpbin.org/post") Proxy: req.Proxy = "http://127.0.0.1:8080" // req.Proxy = "https://127.0.0.1:8080" // req.Proxy = "socks5://127.0.0.1:57341" resp, err := req.Get("http://httpbin.org/get") or https://github.com/mozillazg/request/tree/develop/_example/proxy HTTP Basic Authentication: req.BasicAuth = request.BasicAuth{"user", "passwd"} resp, err := req.Get("http://httpbin.org/basic-auth/user/passwd") 标签:GoRequest

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值