pointers - 如果 resp 是一个指向响应对象的指针,为什么我们不必使用 resp* 来访问它的值呢?
问题描述
我最近开始研究 Golang 和随附的文档。在Golang net/http 文档中,Get 方法是:
func Get(url string) (resp *Response, err error)
据我了解,此方法返回一个指向响应对象或错误对象的指针(如果发生错误)。如果resp
是指向响应对象的指针,为什么可以resp
使用以下代码访问该值:
func main() {
resp, err := http.Get("http://google.com")
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
fmt.Println(resp)
}
不应该fmt.Println(*resp)
改为吗?在整个文档中还有许多其他类似的示例。我以为我理解了指针,但我显然遗漏了一些东西。澄清这一点的任何帮助肯定会受到赞赏。
解决方案
如果
resp
是指向响应对象的指针,为什么可以使用 [ ] 访问 [对象本身fmt.Println(resp)
] ...不应该fmt.Println(*resp)
改为吗?
如果你发送fmt.Println
一个指向一个对象的指针,fmt.Println
可以使用指针到达对象本身(即访问它——甚至修改它,但fmt.Println
不修改它)。
如果发送到对象fmt.Println
的副本,fmt.Println
可以使用该对象的副本,即访问它(并且不能修改原始对象)。
所以从这个意义上说,给fmt.Println
指针值严格来说比传递对象的副本更强大,因为它可以修改对象。fmt
代码没有使用这种能力,但它也存在于您可能传递指针的任何其他地方。但只要fmt.Println
:
- 注意到这是一个指针,然后
- 跟随指针访问底层对象,
thenfmt.Println
可以在指向对象的指针和对象的副本上表现相同。
事实上,fmt.Print*
函数族的行为与指针对象和对象副本的行为方式并不完全相同:
package main
import (
"fmt"
)
type T struct {
Name string
Value int
}
func main() {
obj := T{Name: "bob", Value: 42}
fmt.Println(&obj, obj)
fmt.Printf("%#v %#v\n", &obj, obj)
}
当它运行时(在 Go Playground 上尝试),它会打印:
&{bob 42} {bob 42}
&main.T{Name:"bob", Value:42} main.T{Name:"bob", Value:42}
也就是说,使用%v
or获得的默认格式会fmt.Println
打印:
{bob 42}
(对象的副本)或:
&{bob 42}
(指向对象的指针)。使用获得的替代格式%#v
添加类型,以便您获得:
main.T{Name:"bob", Value:42}
(对象的副本)或:
&main.T{Name:"bob", Value:42}
我们在这里看到的是fmt.Println
,它接受一个interface{}
值,经历了以下过程:
检查值的类型。是指针吗?如果是这样,请记住它是一个指针。如果它是一个 nil 指针,则打印
<nil>
并且不要继续;否则,获取指针指向的对象。现在它不是指针:值有什么类型?如果它是一个
struct
类型,打印出它的类型名称 (%#v
) 或不是 (%v
),以 if step 1 后跟一个指针为前缀&
,然后是左大括号和结构内事物的值列表,然后是右大括号结束整个东西。使用时
%#v
,打印字段名称并以适合用作 Go 源代码的格式打印值。否则,只打印字符串和int
s 的内容等等。
其他指针类型并不总是得到相同的处理!例如,添加一个int
变量,将其设置为某个值,然后调用fmt.Println(&i, i)
. 请注意,这次你没有得到&42 42
或类似的东西,而是0x40e050 42
或类似的东西。fmt.Printf
用and试试这个%#v
。所以输出取决于类型和格式化动词。
如果调用必须修改其对象的函数(例如 中的scan
族fmt
),则必须传递一个指针,因为它们需要访问对象才能修改它们。
每个可以接受无约束interface{}
类型值的函数(包括这里的Print*
andScan*
系列中的所有内容)都必须记录它们对每个实际类型所做的事情。如果他们像Print*
家族那样说,当给定一个指向结构类型的指针时,它们会跟随指针(如果不是 nil),这让您知道您可以发送指针而不是对象。
(某些库中的某些函数会因对它们所做的工作的记录不足而感到内疚,您必须进行实验。这通常不是一个好情况,因为实验的结果可能是当前实现的意外,而不是承诺的行为将来不会改变。这是谨慎使用的原因之一interface{}
:这意味着您必须编写大量文档。)