对于我们中的一些人来说,如果不是从低级别的高级语言(如C)开始编程,"指针 "的概念是陌生的。当我开始学习Go时,我花了一段时间才完全掌握了 "指针 "是什么。我先是在Golang中理解了这个概念,然后回到C和C++中,意识到这是同样的东西!在这里,我试图解释一下这个概念。
以下是我对 "指针 "的解密尝试。但在此之前,让我们来谈谈地址。
地址:代码和数据的生存空间
一个地址是对一个特定内存位置的引用。代码和变量被加载到内存中供处理器使用。对于像a + b (假设a 和b 是整数或任何在特定语言中可以操作的操作数+ )这样的简单计算是可能的。处理器必须知道它们在内存中的精确位置,这样它就可以挑选它们,然后对它们进行加法;处理器必须知道这些变量的地址。
指针:邻里关系专家
那么,什么是指针?形象地说,指针就像邻居家的白胡子。他是一个知道邻居中的每个人,他们住在哪里,他们做什么,等等。如果你想知道变量a 的地址,就可以去找他。
在Golang中,一个指针持有一个值的内存地址。这就是它的全部作用。
这里有一个例子:
package main
import "fmt"
func main() {
var number int = 33
var addressOfNumber *int = &number // Pointer to the `number` variable
fmt.Print(addressOfNumber)
}
// Prints: 0xc0000b4008
在Go中,& 操作符产生了一个指向其操作数的指针,这样&number 就变成了指向变量number 的指针。&number 知道number 住在0xc0000b4008 。
var addressOfNumber *int = &number 是如何声明一个指针变量的。一个隐式声明是这样的: 。addressOfNumber := &number
好的。所以,现在我们知道指针在内存中保存着数值的地址。
棒极了!
那么,重点是什么呢?(无双关之意)
为了说明指针的意义,让我们假设你在一个工资为1500美元的地方工作。一个新的老板来了,要求你的初级开发人员写一个程序,使每个人的工资翻倍!大家都很兴奋。大家都很兴奋,你也很高兴!
但是,你的开发人员带着这个来了,要求进行代码审查:
package main
import "fmt"
func doubleWage(wage int) {
wage *= 2
}
func main() {
var wage int = 1500 // Salary in USD
doubleWage(wage)
fmt.Println(wage)
}
看了这个,你认为你的新工资会是多少?1,500美元还是3,000美元?乍一看,这看起来会使你的工资翻倍,达到3000美元,但事实并非如此!而这正是指点迷津的时候了。
为什么我们没有得到加薪
我们的工资在之前的项目中保持不变,即1500美元。这是因为Golang是一种按价值传递的语言。Pass-by-value是一种评估策略,根据维基百科的说法。
在逐值传递中,参数表达式被评估,得到的值被绑定到函数中的相应变量上(经常是通过将值复制到一个新的内存区域)。如果函数或过程可以为其参数赋值,则只对其局部变量进行赋值。也就是说,当函数返回时,任何传入函数调用的东西在调用者的范围内都是不变的。
所以这就是我们上面的程序中发生的事情:
func main() {
var wage int = 1500 // Original value of `wage`
doubleWage(wage) // COPY `wage`
fmt.Println(wage) // Print original value of `wage`
}
doubleWage(wage) 完成了工资翻倍的工作,但我们对翻倍后的数值没有做任何处理!
为加薪而战
有一个特殊的运算符我们还没有提到......* 。当我们把它与指针一起使用时,我们不是得到某个值的地址,而是得到一个值本身。
从这一点来看:
var number int = 33
var addressOfNumber *int = &number // Pointer to the `number` variable
我们知道addressOfNumber 是一个指针(返回number 的地址),如果我们做*addressOfNumber ,我们会得到33 ,即值本身。我们可以继续做类似于*addressOfNumber = 55 的事情,这将把number 的值设置为55;我们把这称为 "取消引用"。
有了新发现的知识,我们可以在这里修复我们的原始程序,以获得我们应得的报酬:
package main
import "fmt"
func doubleWage(wage *int) {
*wage *= 2
}
func main() {
var wage int = 1500 // Salary in USD
doubleWage(&wage)
fmt.Println(wage)
}
// Prints 3000!
我们已经让我们的doubleWage 接受一个指针作为参数,并且用*wage *= 2 修改了指针所指向的东西。现在要使用它,因为doubleWage 指向一个地址,我们必须用&wage 传递一个地址给它,这就够了。
指针允许我们跳转范围,而这,女士们先生们,就是指针的意义所在。