在C/C++ 中我们就学习过指针,但是C/C++语言中的指针可以自由地进行偏移,甚至会指向一些操作系统的核心区域,如果代码书写者不注意可能会造成指针的越界访问,有着相当大的安全风险。
在Go语言中,指针被拆分为两个核心概念:
-
类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。
-
切片,由指向起始元素的原始指针、元素数量和容量组成。
- 切片比原始指针具备更强大的特性,而且更为安全。
- 切片在发生越界时,运行时会报出宕机,并打出堆栈,而原始指针只会崩溃。
受益于这样的约束和拆分,Go语言的指针类型变量即拥有指针高效访问的特点,又不会发生指针偏移,从而避免了非法修改关键性数据的问题。同时,垃圾回收也变得较为容易
指针相关语句格式
// 对指针进行声明
var p *int /* 指向整型*/
var q *float32 /* 指向浮点型 */
// 对变量进行取地址操作
ptr := &a
// 对指针进行取值操作
b := *ptr
指针相关概念
-
一个指针变量可以指向任何一个值的内存地址
- 它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节
- 指针的内存大小只取决于所指向的变量的地址本身占用的内存大小
- 这个地址代表变量在内存中的位置
- 每个变量都拥有地址,指针的值就是地址
-
当一个指针被定义后没有分配到任何变量时,它的默认值为 nil
- nil 指针也称为空指针。
- nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值
-
Go语言中使用在变量名前面添加
&操作符(前缀)来获取变量的内存地址(取地址操作)-
ptr := &a // 其中 a 的类型为 T - 其中 a 代表被取地址的变量,变量 a 的地址使用变量 ptr 进行接收,ptr 的类型为
*T,称做 T 的指针类型,*`代表指针
-
指针相关操作
-
当使用
&操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用*操作符,也就是指针取值- 基本与C/C++语言相同
-
// 可以用 %T 打印ptr的类型 fmt.Printf("ptr type: %T\n", ptr) -
// 可以打印ptr的指针地址 fmt.Printf("address: %p\n", ptr)
-
通过指针不仅可以取值,也可以修改值
- 这个也与C/C++语言中的指针基本相同
指针数组
// 定义一个数组 a
a := []int{10,100,200}
// 声明一个整型指针数组
var ptr [3]*int
// 整数地址赋值给指针数组
for i = 0; i < MAX; i++ {
ptr[i] = &a[i]
}
指向指针的指针
-
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量
-
当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址
- p1 --> p2 --> 变量
-
指向指针的指针变量声明格式如下:
-
var ptr **int; - 访问指向指针的指针变量值需要使用两个 * 号
-
指针作为函数参数
Go 语言允许向函数传递指针,只需要在函数定义的参数上设置为指针类型即可
以下实例演示了如何向函数传递指针,并在函数调用后修改函数内的值
实例:
package main import "fmt" func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 fmt.Printf("交换前 a 的值 : %d\n", a ) fmt.Printf("交换前 b 的值 : %d\n", b ) /* 调用函数用于交换值 * &a 指向 a 变量的地址 * &b 指向 b 变量的地址 */ swap(&a, &b); fmt.Printf("交换后 a 的值 : %d\n", a ) fmt.Printf("交换后 b 的值 : %d\n", b ) } func swap(x *int, y *int) { var temp int temp = *x /* 保存 x 地址的值 */ *x = *y /* 将 y 赋值给 x */ *y = temp /* 将 temp 赋值给 y */ }以上实例允许输出结果为:
交换前 a 的值 : 100 交换前 b 的值 : 200 交换后 a 的值 : 200 交换后 b 的值 : 100
附录
本文参考链接及推荐阅读: