在程序中,变量分为变量名和变量内容,变量内容的存储一般分配到堆和栈中。在Go语言中,传递变量的方式有两种:值传递和引用传递。其中,按值传递会将变量内容直接附加到变量名上,而按引用传递会将变量内容的地址附加到变量名上。在Golang中是怎么做的如果面试的时候面试官问你:“Go的参数是怎么传的?”你会怎么回答?这个问题其实只有一个答案。因为在Golang中所有的类型传递都是通过值传递而不是引用传递来实现的,即使是指针的传递也是通过复制指针的方式来进行的。另外,对于一些包裹了底层数据的数据结构,在传值过程中,只拷贝了实例指针,并没有拷贝底层数据暴露出来的指针。下面以Go1.8版本的slice为例简单理解一下:溢出||内存>最大分配||长度<0||len>cap{//注意:当有人make([]T,bignumber)时,产生一个“lenoutofrange”错误而不是一个//“capoutofrange”错误。//'capoutofrange'也是正确的,但是因为cap只是隐式提供//,所以说len更清楚。//请参阅golang.org/issue/4085。记忆,溢出:=数学。MulUintptr(et.size,uintptr(len))如果溢出||内存>最大分配||len<0{panicmakeslicelen()}panicmakeslicecap()}returnmallocgc(mem,et,true)//applymemory}查看slice在初始化过程中,会调用runtime中的makeslice函数,这个函数会返回接受变量的切片地址。typeslicestruct{arrayunsafe.Pointer//底层数组的地址lenintcapint}//初始化过程p:=make([]int,0)fmt.Printf("addressofvariablep%p",&p)上面的fmt.Printf("addressofslice%p\n",p)打印的是内容,这是因为Go内部实现了自动解引用(即Go内部实现的解引用操作)。Receive在自动取消引用时从指针类型转换为值类型。顺便说一句,当自动获取引用时,接收者会从值类型转换为指针类型。如果未实现自动解除引用会怎样?下面是没有实现自动解引用的情况://当我们打印变量p的时候,实际过程有这样的变化//只是猜测,当然解引用是一定的//&addressoperator//*基于的操作关于地址值也叫解引用算法,间接运算符//1.获取指针地址&p//2.获取数组地址&((&p).array)//3.获取底层数组实际内容*&((&p).array)没有实现自动借用的函数传递过程,也是通过复制指针传递的。内容如下:packagemainimport("fmt")funcchange(p1[]int){fmt.Printf("Thememoryaddressofp1is:%p\n",&p1)//p1的内存地址为:0xc0000a6048fmt.Printf("函数中接收到的slice的内存地址为:%p\n",p1)//函数中接收到的slice的内存地址为:0xc00008c030p1=append(p1,30)}funcmain(){p:=make([]int,3)//抛出一个指针p=append(p,20)fmt.Printf("p的内存地址为:%p\n",&p)//p的内存地址为:0xc00009a018fmt.Printf("slice的内存地址为:%p\n",p)//slice的内存地址为:0xc00008c030change(p)//重新生成一个地址p1指向slice地址fmt.Printf("修改后p的内存地址%p\n",&p)//p修改后的内存地址为0xc00009a018fmt.Printf("修改后的切片内存地址%p\n",p)//修改后的切片内存地址为0xc00008c030fmt.Println("modifiedslice:",p)//modifiedslice[00020]fmt.Println(*&p)//[00020]}需要注意的是,在函数的处理过程中ofpassing,复制的不是slice内部底层数组的指针,而是makeSlice函数返回的指针源代码。当你阅读一些旧文章时,你可能看到过这样一句话:makereturnssliceinstanceof。但事实上,这种说法已经过时了。Golang1.2之后,make返回的是实例指针。githubpr地址:https://github.com/golang/go/...其实扩展类似于slice和map,chan。先说map,官网对map的定义:“Go提供了一个内置的map类型,实现了哈希表。映射类型是引用类型,如指针或切片。”而chan也是和map一样的指针,也就是说,两者的原理类似slice。funcmakemap(t*maptype,hintint,h*hmap)*hmap{mem,overflow:=math.MulUintptr(uintptr(hint),t.bucket.size)如果溢出||内存>maxAlloc{提示=0}。..}funcmakechan(t*chantype,sizeint)*hchan{...mem,overflow:=math.MulUintptr(elem.size,uintptr(size))如果溢出||内存>maxAlloc-hchanSize||size<0{panic(plainError("makechan:sizeoutofrange"))}...}如果平时使用不注意,会有一些不必要的麻烦,比如:packagemainimport"fmt"typeInfoInsstruct{Namestringinfo[]string}funcNewInfoIns()InfoIns{returnInfoIns{Name:"",info:nil,}}func(n*InfoIns)SetInfo(info[]string){n.info=info}}funcmain(){infoIns:=NewInfoIns()info:=[]string{"p1","p2","p3"}infoIns.SetInfo(info)info[1]="p4"fmt.Println(infoIns.info)//[p1p4p3]}这里我SetInfo之后,nfoIns存放的是info的地址。之后一旦信息发生变化,InfoIns中的内容也会随之变化。解决方法是在SetInfo时重新申请地址。func(n*InfoIns)SetInfo(info[]string){n.info=make([]string,len(info))copy(n.info,info)}FootnoteGoland查看Go源码的方法:Ctrl+Shift+f全局搜索,选??择ALLPlaceinScope。推荐看一篇文章讲IP地址的那些事。Golang常用设计模式之装饰模式
