指针类型
之前并没有学过C家族的语言,不知其中指针的可怕,但在浏览各种技术博客时时会经常看到类似的字眼,于是对指针这个词有了一种莫名的高大上的感觉。其实Java中也是存在指针的概念的,但不过Java中封装的思想将它很好的隐藏起来了。哈哈,跑题了,来看看GO语言中的指针吧。
指针
从比变量的角度来看指针,一个变量我们经常看到的是值,比如var int1 int=100,100就是int1的值,而int1存在内存中,系统会为其划分一块空间,这块空间有一个编号,这个编号就是内存地址。而指针就是存储这个内存地址的变量。
查看内存地址
首先还是看上面的int1,我们通过格式化输出的方式查看它的值和地址:
fmt.Printf("int1的值=%d,地址=%v\n",int1,&int1) //%d:以十进制方式输出 %v:以值的默认格式输出
这里用到了取地址符&,下面再说,看结果:
int1的值=100,地址=0x14000124008
可以看到打印出了一个16进制数来表示int1的内存,注意的是,指针的大小与它指向的值无关,只与你的机器有关,在32位机器上占用4个字节,而在64位机器上占用8个字节。
定义指针
说了这么多,接下来就来声明一个指针,这里先要提到两个符号:
- 取地址符&,取出变量的地址,上面的格式化输出就有用到
- 取值符*,可以表示为当前变量是一个指针类型,也可以将当前指针指向的变量值取出,和&为互逆操作
指针变量的标志就是*变量名:
var pointer *int1=&int1 //此处*表示一个int指针类型
这样我们就定义好了一个int1的指针,接下来输出它:
fmt.Printf("pointer的值=%v,地址=%v\n",pointer,&pointer)
结果:
指针的值=0x14000124008,地址=0x14000126020
上面我们输出了指针pointer的值,以及该指针的内存地址,有点绕,哈哈。是的,因为指针也是一种变量,所以它也有自己的内存地址,不过它占用的空间非常小。
为了方便理解,我们通过一张表来列出上面的结果:
变量名 | 变量值 | 地址 |
---|---|---|
int1 | 100 | 0x14000124008 |
pointer | 0x14000124008 | 0x14000126020 |
一句话归纳就是,指针存储的是它指向变量的内存地址,而指针本身也有自己的内存地址
通过指针赋值改变其指向变量的值
上面我们已经提到,指针存储的是它指向的变量的内存地址,那么我们必然可以通过指针来改变它指向的这个变量的值,很绕,哈哈,但是应该不难理解。
还是拿int1为例子:
fmt.Println(int1)
*pointer=200 //这里*表示当前指针所指向的变量的值,这里相当于int1=200
fmt.Pringln(int1)
结果:
100
200
说明通过指针改变其指向的变量值成功
我们再看一个例子:
var int2 int=123
var pointer2 *int=&int2
fmt.Println(*pointer2,int2)
结果:
123 123
上面的例子演示了*的两个作用,当我们定义一个指针时,此时它表示这是一个指针类型,比如上面的pointer2,而当我们需要将当前指针所指向的变量的值取出时,它也可以实现,比如最后我们打印两个变量,发现结果是一样的。但值得注意的是,我们不能将一个变量的值赋给指针:
pointer2=int2
和声明其他变量不同,指针类型不允许声明一个空的,因为它没有零值:
var pointer3 *int
*pointer3=100
值类型,引用类型
提到指针,那么必然会联系到值类型和引用类型了。简单来说,值类型在我们操作它时,会改变它本身的值,而引用类型当我们操作它时,实际上操作的是它的一个副本,因此它本身的值并不会变。而通过指针,我们可以指向它的内存地址,从而实现对它本身值的改变。
GO语言中的数据类型基本上都是值类型,来看下面的分类:
- 值类型:整形、浮点型、bool、array、string、数组、结构体
- 引用类型:指针、slice、map、channel、interface
灵活使用指针可以提高程序的效率,具体体现在后续聊到函数、方法时,我们就会有感受了。