06.Go 指针

一个指针变量指向了一个值的内存地址。类似于变量和常量,在使用指针前需要声明指针。指针声明格式如下:var var_name *var-type

  • var-type为指针类型
  • var_name为指针变量名
  • *号用于指定变量是作为一个指针。
var ip *int        /*指向整型*/
var fp *float32    /*指向浮点型*/

指针在Go语言中可以被拆分为两部分内容:

  • 类型指针:允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须复制数据,类型指针不能进行偏移和运算。
  • 切片:由指向起始元素的原始指针、元素数量和容量组成。

所有指针的值的实际数据类型(无论是整数、浮点数还是其他数据类型)都是相同的,它表示内存地址的长十六进制数。

使用指针基本上分为3个步骤:定义一个指针变量,将一个变量的地址赋值给一个指针,最后访问指针变量中可用地址的值:

func main() {
    a := 20
    ap := &a
    fmt.Printf("a的地址:%x\n", &a)
    fmt.Printf("ap的地址:%x\n", ap)
    fmt.Printf("*ap的值:%d\n", *ap)
}

1、指针地址和指针类型

一个指针变量可以指向任何一个值的内存地址,它所指向的值的内存地址在32位和64位机器上分别占用4B或8B,占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时,它的默认值为nil。指针变量通常缩写为ptr。

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用“&”操作符放在变量前面对变量进行取地址操作,格式如下:ptr := &v //v 的类型为 T

  • v代表被取地址的变量
  • 变量v的地址使用变量ptr进行接收,ptr的类型为“*T”,称为T的指针类型
  • “*”代表指针。

指针使用流程如下:

  • ① 定义指针变量。
  • ② 为指针变量赋值。
  • ③ 访问指针变量中指向地址的值。
func main() {
    var cat int = 1
    var fruit string = "banana"
    fmt.Printf("%p %p", &cat, &fruit)
}

  • 使用fmt.Printf的动词“%p”打印cat和str变量取地址后的指针值,指针值带有“0x”的十六进制前缀。
  • 注意:变量、指针和地址的关系是,每个变量都拥有地址,指针的值就是地址。
2、指针的创建

可以使用new()函数来创建指针,格式如下:new(类型)

new()函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

func main() {
	str := new(string)
	*str = "哎一古,金社长!"
	fmt.Println(*str)
	fmt.Println(&str)
}

3、指针的特点

① 在赋值语句中,*T如果出现在“=”左边,表示指针声明;*T如果出现在“=”右边,则表示取指针指向的值,例如:

var m = 20
p := &m   //*p和m的值都为20

② 结构体指针访问结构体字段仍然使用“.”操作符,Go语言中没有“->”操作符,例如:

func main() {
    type User struct {
        name string
        age  int
    }

    jerry := User{
        name: "jerry",
        age:  20,
    }
    p := &jerry
    fmt.Println(p.name)	// 输出jerry
}

③ Go语言中不支持指针的运算。Go语言支持垃圾回收机制,如果再支持指针运算,则会给垃圾回收的实现带来不便,又由于指针运算在C和C++中很容易出现问题,因此Go语言直接禁止指针运算

a := 1234
p := &a
p++    		//这种写法是不允许的,系统会报"non-numeric type *int"错误

④ 函数中允许返回局部变量的地址。Go语言的编译器使用“栈逃逸”机制将这种局部变量的空间分配在堆上

func main() {
	fmt.Println(sum1(1, 2))		// 输出:0xc00000a0c8
}
func sum1(a, b int) *int {
	sum := a + b
	return &sum // 这种情况是允许的,sum会分配在heap上
}
4、获取指针的值

当使用“&”操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用“*”操作符,也就是指针取值。

func main() {
	// 准备一个字符串类型
	var house = "遥遥领先"
	// 对字符串取地址,ptr类型为*string
	ptr := &house

	// &house是取house的内存地址 赋值给 ptr,所以ptr的值是house的内存地址
	fmt.Println("\nhouse变量的内存地址:        ", ptr)
	// *ptr 是 取内存地址里面存储的内容
	fmt.Println("house变量的内存地址里面的值:", *ptr, "\n")

	// 打印ptr的类型
	fmt.Printf("ptr type: %T\n", ptr)
	// 打印ptr的指针地址
	fmt.Printf("ptr value: %p\n", ptr)

	fmt.Println("\n--------------取出 ptr 内存地址里面的值,并赋给变量 value-------------- \n")
	// 对指针进行取值操作
	value := *ptr
	// 取值后的类型
	fmt.Printf("value type:  %T\n", value)
	// 指针取值后就是指向变量的值
	fmt.Printf("value value: %s\n", value)
}

变量(house)、指针地址、指针变量(ptr)、取地址(&house)、取值(*ptr)的相互关系和特性如下:

  • 对变量进行取地址操作使用“&”操作符,可以获得这个变量的指针变量。指针变量的值是指针地址,既变量的内存地址值。
  • 对指针变量进行取值操作使用“*”操作符,可以获得指针变量指向的原变量的值。既原变量内存地址保存的值。
5、使用指针修改值

通过指针不仅可以取值,还可以修改值。

使用指针也可以进行数值交换:

func main() {
	swap_num_by_pointer()
}

func swap(a, b *int) {
	// 取a指针的值,赋给临时变量t
	t := *a
	// 取b指针的值,赋给指针变量*a
	*a = *b
	// 将临时变量t 赋值给指针变量*b
	*b = t
}

func swap_num_by_pointer() {
	// 准备两个变量,x=2 和 y=5
	x, y := 2, 5
	fmt.Println("\n交换前两个变量的值:", "x =", x, ", y =", y)
	// 交换变量值
	swap(&x, &y)
	// 输出变量的值
	fmt.Println("交换后两个变量的值:", "x =", x, ", y =", y)
}

“*”操作符作为右值时,意思是取指针的值;作为左值时,也就是放在赋值操作符的左边时,表示a指向的变量。总的来说,“*”操作符的根本意义就是操作指针指向的变量。当操作在右值时,就是取指向变量的值,当操作在左值时,就是将值设置给指向的变量。

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值