Go 语言入门指南:基础语法和常用特性解析4 | 青训营

64 阅读4分钟

在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

附录

本文参考链接及推荐阅读:

Go 语言基础语法 | 菜鸟教程 (runoob.com)

Go语言基本语法 (biancheng.net)