Go语言 值,指针,引用类型


Go语言的指针与C或C++的指针类似,但是Go语言的指针不支持指针运算,这样就消除了在C或C++程序中一些潜在的问题。由于Go语言有自己的垃圾回收器,并且会自动管理内存,所以Go语言也不需要像C或C++一样使用free函数或者delete操作符。

Go语言的指针创建后可以像Java和Python中对象的引用一样使用。

在Go语言中,对于布尔变量或数值类型或字符串类型或数组都是按照值传递的:值在传递给函数或者方法时会被复制一份,然后方法或函数使用的是复制的这份值,也就不会对原值产生什么影响。一般情况下,对于布尔变量或数值类型或字符串类型的按值传递是非常廉价的,Go语言编译器会在传递过程中进行安全优化。

但是在Go语言中,字符串是不可变的,因此在进行修改字符串时(例如使用+=操作),Go语言必须创建一个新的字符串,然后复制原始的字符串并将其添加到新字符串之后,对于大字符串来说,操作的代价可能会比较大。

对于大字符串是这样,对于数组进行值传递也是如此。为了解决可能产生的巨大代价,Go语言使用数组切片来代替数组的使用。传递一个切片的代价跟传递字符串差不多,无论该切片的长度或容量是多大。对切片进行复制修改操作也不会像字符串那样需要创建新的切片,因为切片是可变的,属于引用类型。

func main() {
   a := 3
   b := 4
   c := "abc"
   d := [3]int{1,2,3}
   fmt.Printf("main方法:a的值为 %v,b的值为 %v,c的值为 %v,d的值为 %v \n",a,b,c,d)
   demo(a,b,c,d)
   fmt.Printf("main方法:a的值为 %v,b的值为 %v,c的值为 %v,d的值为 %v \n",a,b,c,d)
}

func demo(a,b int,c string,d [3]int) {
   a = 5
   b = 6
   c = "efg"
   d[0] = 0
   fmt.Printf("demo函数:a的值为 %v,b的值为 %v,c的值为 %v,d的值为 %v\n",a,b,c,d)
}
----output----
main方法:  a的值为 3,b的值为 4,c的值为 abc,d的值为 [1 2 3]
demo函数: a的值为 5,b的值为 6,c的值为 efg,d的值为 [0 2 3]
main方法:  a的值为 3,b的值为 4,c的值为 abc,d的值为 [1 2 3]

Go语言中的引用类型有:映射(map),数组切片(slice),通道(channel),方法与函数。

由于Go语言存在垃圾回收器,因此在一个本地变量不再被使用时(不再被引用或者不在作用于范围)就会被垃圾回收器回收掉,这时本地变量的生命周期由它们的作用域决定。那如果我们想要管理本地变量的生命周期呢?这时就需要使用指针来管理本地变量,只要该变量至少存在一个指针,那么该变量的生命周期就可以独立于作用域。

使用指针能让我们控制变量的生命周期,不受作用域的影响,另外变量在传递过程中成本最小化,且可以轻易的修改变量的内容,而不是对复制的值进行操作。指针是一个变量,这个变量实际上是保存了另一个变量的内存地址,任何被指针保存了内存地址的变量都可以通过指针来修改内容。指针的传递非常廉价。

在使用指针前,我们需要明白两个操作符的含义
①操作符& : 当作二元操作符时,是按位与操作;当作一元操作符时,是返回该变量的内存地址。
②操作符* : 当作二元操作符时,是相乘的操作;当作一元操作符(解引用操作符)时,是返回该指针指向的变量的值,其实就是解除变量的指针引用,返回该变量的值。

指针的创建与使用,可以看下面的代码实例

func main() {
   a := 3
   p := &a //这里是获取变量a的内存地址,并将其赋值给变量p
   fmt.Printf("a的值为 %v, a的指针是 %v ,p指向的变量的值为 %v\n",a,p,*p)
}
-----output-----
a的值为 3, a的指针是 0xc042060080 ,p指向的变量的值为 3

其实*p和变量a的值是相等的,两者可以交换着使用,两者都与同一块内存地址相关联,任意一个变量进行修改操作都会影响到另一个变量的值,但是若变量p被赋值其他变量的指针就不行了。

关于指针的综合运用,我们看以下的代码实例

func main() {
   a := 3
   b := 4
   p1 := &a //获取变量a的内存地址,并将其赋值给变量p1
   p2 := &b //获取变量b的内存地址,并将其赋值给变量p2
   fmt.Printf("a的值为 %v, a的指针是 %v ,p1指向的变量的值为 %v\n",a,p1,*p1)
   fmt.Printf("b的值为 %v, b的指针是 %v ,p2指向的变量的值为 %v\n",b,p2,*p2)
   fmt.Println(demo(p1,p2))
   fmt.Printf("a的值为 %v, a的指针是 %v ,p1指向的变量的值为 %v\n",a,p1,*p1)
   fmt.Printf("b的值为 %v, b的指针是 %v ,p2指向的变量的值为 %v\n",b,p2,*p2)
}

func demo(a,b *int)int  {
   *a = 5
   *b = 6
   return  *a * *b //这里出现连续的两个*,Go编译器会根据上下文自动识别乘法与两个引用
}
-----output-----
a的值为 3, a的指针是 0xc042060080 ,p1指向的变量的值为 3
b的值为 4, b的指针是 0xc042060088 ,p2指向的变量的值为 4
30
a的值为 5, a的指针是 0xc042060080 ,p1指向的变量的值为 5
b的值为 6, b的指针是 0xc042060088 ,p2指向的变量的值为 6

根据上面代码,我们可以看到使用指针后,本来是值传递的整数类型在函数或方法中修改会影响到原变量的值。

空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
查看以下实例:

package main

import "fmt"
func main() {
   var  ptr *int
   fmt.Printf("ptr 的值为 : %x\n", ptr  )
}

以上实例输出结果为:

ptr 的值为 : 0

空指针判断:

if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

多重间接引用
以上面代码为例, 在a := 3, p1 := &a中,p1是指向a的内存地址,这种叫做间接引用,若还有一个p2是指向p1的内存地址,p1指向a的内存地址,这种就叫做多重间接引用,不管哪种引用,若其中一个变量进行修改内容操作,均会影响到其他所有变量的内容。

func main() {
   a := 3
   p1 := &a  //p1是指向变量a内存地址的指针
   p2 := &p1 //p2是指向变量p1内存地址的指针
   fmt.Printf("a:%v, p1:%v, *p1:%v, p2:%v, **p2:%v\n",a,p1,*p1,p2,**p2)
   a = 4
   fmt.Printf("a:%v, p1:%v, *p1:%v, p2:%v, **p2:%v\n",a,p1,*p1,p2,**p2)
}
-----output-----
a:3, p1:0xc0420080b8, *p1:3, p2:0xc042004028, **p2:3
a:4, p1:0xc0420080b8, *p1:4, p2:0xc042004028, **p2:4

new函数与&操作符
Go语言中提供两种创建变量的方式,同时可以获得指向它们的指针:new函数与&操作符。这两种的使用方式如下面代码所示

type Person struct {
   name string
   sex  string
   age int
}
func main() {
   person1 := Person{"zhangsan","man",25} //创建一个person1对象
   person2 := new(Person)//使用new创建一个person2对象,同时获得person的指针
   person2.name,person2.sex,person2.age = "wangwu","man",25
   person3 := &Person{"lisi","man",25}//使用&创建一个person3对象,同时获得person的指针
   fmt.Printf("person1:%v, person2:%v, person3:%v\n",person1,person2,person3)
}
-----output-----
person1:{zhangsan man 25}, person2:&{wangwu man 25}, person3:&{lisi man 25}

从输出结果来看,new函数与&操作符两种方式区别不大,&操作符创建起来更加的简洁,并且随时可以指定属性初始值。Go语言打印指向person的指针时,会打印person属性的具体内容,并且在前缀上加上&表示该变量是一个指针。

接下来我们来传递一个结构体指针,并修改结构体的属性,看看Go语言是如何操作的。

type Person struct {
   name string
   sex  string
   age int
}
func main() {
   person1 := Person{"zhangsan","man",25} //创建一个person1对象
   fmt.Printf("person1:%v\n",person1)
   demo(&person1)
   fmt.Printf("person1:%v\n",person1)
}

func demo(person *Person)  {
   (*person).age = 18 //显示的解引用
   person.name = "GoLang" //隐式的解引用
}

main函数中创建一个pserson1对象,设置初始化属性后将它的指针传递到demo函数中,大家可以看到demo函数中有两种解引用(就是将一个指针转化为原对象)的方式,第一种: (*person).age是显示的解引用,第二种是使用 "." 操作符自动的将指针解引用,使用上这两种没什么区别,但第二种更简单。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  •   引用类型的值(对象)是引用类型的一个实例。   在 ECMAscript 中,引用类型是一种数据结构,用于将数...
    霜天晓阅读 1,035评论 0 1
  • 我还不知道这篇文章意味着什么。我只是万千星辉中的一点,星辰大海要三分了吗? 我看不到原来的你们,也许我喜欢的,只是...
    一见钟情一世相守阅读 339评论 0 0