参考文献

计算机存储单位关系:https://blog.mailjob.net/posts/506822510.html

地址与指针

何为内存?

(注意,我们这里提到的内存并不是人们常说的计算机的物理内存,而是虚拟的逻辑内存空间)

简单点说:地址就是可以唯一标识某一点的一个编号,即一个数字!我们都见过尺子,我们统一以毫米为单位,一把长1000毫米的尺子,其范围区间为0~999,而我们可以准确的找到35毫米、256毫米处的位置。同样的道理,内存也如此,也是像尺子一样线性排布,只不过这个范围略大。

在我们最广泛使用的32位操作系统下,是从 0~4,294,967,295 之间,而地址就是这之中的的一个编号而已,习惯上,在计算机里地址我们常常用其对应的十六进制数来表示,比如 0x12ff7c 这样。

在C程序中,每一个定义的变量,在内存中都占有一个内存单元,比如int类型占4个字节char类型占一个字节等等,每个字节都在0~4,294,967,295之间都有一个对应的编号,C语言允许在程序中使用变量的地址,并可以通过地址运算符"&"得到变量的地址。

何为指针?

简单的讲,地址就是逻辑内存上的编号,而指针虽然也表示一个编号,也是一个地址。但两者性质却不相同。一个代表了常量,另一个则是变量。就好比内存是一把尺子,而指针就是尺子上面的游标,可以左右移动,他某一个时刻是指向一个地方的,这就是指针变量。

Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:&(取地址)和*(根据地址取值)。

指针地址和指针类型

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int*int64*string等。

取变量指针的语法如下:

ptr := &v    // v的类型为T

其中:

  • v:代表被取地址的变量,类型为T
  • ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。

举个栗子

package main

import "fmt"

func main() {
	a := 666
	b := &a // 取a的指针地址

	fmt.Printf("%p\n", &a) // a的内存地址  输出:0xc0000b0008
	fmt.Printf("%p\n", b) // b的变量值	输出:0xc0000b0008
	fmt.Printf("%p\n", &b) // b的内存地址  输出:0xc0000aa018
}

image-20210915112747871

指针取值

package main

import "fmt"

func main() {
	a := 666
	b := &a // 取a的指针地址


	fmt.Printf("%p\n", &a) // a的内存地址  输出:0xc0000b0008
	fmt.Printf("%p\n", b) // b的变量值	输出:0xc0000b0008
	fmt.Printf("%p\n", &b) // b的内存地址  输出:0xc0000aa018

	c := *b // 指针取值(根据指针去内存取值)

	fmt.Printf("%p\n", &c) // 输出:0xc000018088
	fmt.Printf("type of c:%T\n", c) // 输出:type of c:int
	fmt.Printf("value of c:%v\n", c) // 输出:value of c:666
}

总结:

取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  • 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

指针传值示例:

package main

import "fmt"

func f1(a int) {
	a = 789
}

// * 代表从内存地址取值
func f2(a *int) {
	*a = 123 // 将内存地址的值重新赋值
}

func main() {
	a := 666

	f1(a)
	fmt.Println(a) // 输出:666

	// 此处传递 a 的内存地址
	f2(&a)
	fmt.Println(a) // 输出:123
}