首页 > 解决方案 > 如果 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)改为吗?在整个文档中还有许多其他类似的示例。我以为我理解了指针,但我显然遗漏了一些东西。澄清这一点的任何帮助肯定会受到赞赏。

标签: pointersgogethttprequest

解决方案


如果resp是指向响应对象的指针,为什么可以使用 [ ] 访问 [对象本身fmt.Println(resp)] ...不应该fmt.Println(*resp)改为吗?

如果你发送fmt.Println一个指向一个对象的指针,fmt.Println可以使用指针到达对象本身(即访问它——甚至修改它,但fmt.Println不修改它)。

如果发送到对象fmt.Println副本fmt.Println可以使用该对象的副本,即访问它(并且不能修改原始对象)。

所以从这个意义上说,给fmt.Println指针值严格来说比传递对象的副本更强大,因为它可以修改对象。fmt代码没有使用这种能力,但它也存在于您可能传递指针的任何其他地方。但只要fmt.Println

  1. 注意到这是一个指针,然后
  2. 跟随指针访问底层对象,

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}

也就是说,使用%vor获得的默认格式会fmt.Println打印:

{bob 42}

(对象的副本)或:

&{bob 42}

(指向对象的指针)。使用获得的替代格式%#v添加类型,以便您获得:

main.T{Name:"bob", Value:42}

(对象的副本)或:

&main.T{Name:"bob", Value:42}

我们在这里看到的是fmt.Println,它接受一个interface{}值,经历了以下过程:

  1. 检查值的类型。是指针吗?如果是这样,请记住它是一个指针。如果它是一个 nil 指针,则打印<nil>并且不要继续;否则,获取指针指向的对象。

  2. 现在它不是指针:值有什么类型?如果它是一个struct类型,打印出它的类型名称 ( %#v) 或不是 ( %v),以 if step 1 后跟一个指针为前缀&,然后是左大括号和结构内事物的值列表,然后是右大括号结束整个东西。

    使用时%#v,打印字段名称并以适合用作 Go 源代码的格式打印值。否则,只打印字符串和ints 的内容等等。

其他指针类型并不总是得到相同的处理!例如,添加一个int变量,将其设置为某个值,然后调用fmt.Println(&i, i). 请注意,这次你没有得到&42 42或类似的东西,而是0x40e050 42或类似的东西。fmt.Printf用and试试这个%#v。所以输出取决于类型和格式化动词。

如果调用必须修改其对象的函数(例如 中的scanfmt),则必须传递一个指针,因为它们需要访问对象才能修改它们。

每个可以接受无约束interface{}类型值的函数(包括这里的Print*andScan*系列中的所有内容)都必须记录它们对每个实际类型所做的事情。如果他们像Print*家族那样说,当给定一个指向结构类型的指针时,它们会跟随指针(如果不是 nil),这让您知道您可以发送指针而不是对象。

(某些库中的某些函数会因对它们所做的工作的记录不足而感到内疚,您必须进行实验。这通常不是一个好情况,因为实验的结果可能是当前实现的意外,而不是承诺的行为将来不会改变。这是谨慎使用的原因之一interface{}:这意味着您必须编写大量文档。)


推荐阅读