What are the differences between a *string and a string in Golang?

huangapple go评论101阅读模式
英文:

What are the differences between a *string and a string in Golang?

问题

**目的:**理解在Golang中*stringstring之间的区别


尝试

func passArguments() {
    username := flag.String("user", "root", "Username for this server")
    flag.Parse()
    fmt.Printf("Your username is %q.", *username)
    fmt.Printf("Your username is %q.", username)
}

结果为:

Your username is "root".Your username is %!q(*string=0xc820072200)

但是当将*string赋值给一个string时:

bla := *username
fmt.Printf("Your username is %q.", bla)

它能够再次打印字符串:

Your username is "root".Your username is %!q(*string=0xc8200781f0).Your username is "root".

问题

  1. 为什么*stringstring不相等,例如显示为:"root" vs. %!q(*string=0xc8200781f0)
  2. 在什么其他情况下应该使用*string而不是string,为什么?
  3. 为什么可以将*string赋值给string变量,而字符串的显示不同,例如显示为:"root" vs. %!q(*string=0xc8200781f0)
英文:

Aim: understanding the difference between *string and string in Golang


Attempt

func passArguments() {
	username := flag.String("user", "root", "Username for this server")
	flag.Parse()
	fmt.Printf("Your username is %q.", *username)
	fmt.Printf("Your username is %q.", username)
}

results in:

Your username is "root".Your username is %!q(*string=0xc820072200)

but when the *string is assigned to a string:

bla:=*username
fmt.Printf("Your username is %q.", bla)

it is able to print the string again:

Your username is "root".Your username is %!q(*string=0xc8200781f0).Your username is "root".

Questions

  1. Why is a *string != string, e.g. display of: "root" vs.
    %!q(*string=0xc8200781f0)?
  2. In what other cases should a *string be
    used instead of a string and why?
  3. Why is it possible to assign a
    *string to a string variable, while the display of the string is different, e.g. display of: "root" vs.
    %!q(*string=0xc8200781f0)?

答案1

得分: 18

一个 *string 是一个指向字符串的指针。如果你对指针不熟悉,可以简单地理解为它是一个保存另一个值的地址的值,而不是值本身(它是一种间接级别)。

当在类型中使用 * 时,它表示指向该类型的指针。*int 是指向整数的指针。***bool 是指向指向指向布尔值的指针的指针的指针。

flag.String 返回一个指向字符串的指针,因为它可以在调用 flag.Parse 后修改字符串的值,并且可以使用解引用运算符检索该值 - 也就是说,当在变量上使用 * 时,它会解引用它,或者检索指向的值,而不是变量本身的值(对于指针来说,它只是一个内存地址)。

所以来回答你的具体问题:

  1. fmt 包中的 %q 动词理解字符串(和字节切片),而不是指针,因此显示的是看似无意义的内容(当一个值不是与匹配动词(这里是 %q)期望的类型时,fmt 函数会显示 %!q 以及传递的实际类型和值)。

  2. 很少使用指向字符串的指针。在 Go 中,字符串是不可变的(https://golang.org/ref/spec#String_types),所以在像 flag.String 这样的情况下,你需要返回一个稍后会被修改的字符串,你必须返回一个指向字符串的指针。但在惯用的 Go 代码中很少见到这种情况。

  3. 你没有将 *string(指向字符串的指针)赋值给 string。正如我之前提到的,你正在解引用 *string 变量,提取它的 string 值。所以实际上你是将一个 string 赋值给另一个 string。尝试在那一行上移除 *,你会看到编译器错误消息(实际上,因为你使用了短变量声明符号 :=,你不会看到编译器错误,但你的变量将被声明为指向字符串的指针)。相反,尝试使用以下代码更好地理解正在发生的事情:

     var s string
     s = username
    

这将引发编译器错误。

英文:

A *string is a pointer to a string. If you're not familiar with pointers, let's just say that it's a value that holds the address of another value, instead of the value itself (it's a level of indirection).

When a * is used in a type, it denotes a pointer to that type. *int is a pointer to an integer. ***bool is a pointer to a pointer to a pointer to a bool.

flag.String returns a pointer to a string because it it can then modify the string value (after the call to flag.Parse) and you are able to retrieve that value using the dereference operator - that is, when using * on a variable, it dereferences it, or retrieves the value pointed to instead of the value of the variable itself (which in the case of a pointer would just be a memory address).

So to answer your specific questions:

  1. the %q verb in the fmt package understands strings (and slices of bytes), not pointers, hence the apparent gibberish displayed (when a value is not of the expected type for the matching verb - here %q - the fmt functions display %!q along with the actual type and value passed)

  2. A pointer to a string is very rarely used. A string in Go is immutable (https://golang.org/ref/spec#String_types) so in cases like flag.String where you need to return a string that will be mutated later on, you have to return a pointer to a string. But you won't see that very often in idiomatic Go.

  3. You are not assigning a *string (pointer to a string) to a string. What you are doing, as I mentioned earlier, is dereferencing the *string variable, extracting its string value. So you are in fact assigning a string to a string. Try removing the * on that line, you'll see the compiler error message. (actually, because you're using the short variable declaration notation, :=, you won't see a compiler error, but your variable will be declared as a pointer-to-a-string. Try this instead, to better understand what's going on:

     var s string
     s = username
    

That will raise the compiler error).

huangapple
  • 本文由 发表于 2015年11月16日 01:29:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/33722647.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定