Golang指针之间的区别是什么?

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

Difference between golang pointers

问题

有两种类型的变量我有。检查一下Go playground,我不明白为什么会发生这种情况。问题是:我从Models中得到的应该是一个struct,以便将其用于GORM的First()函数。

代码:

package main

import (
	"fmt"
)

type Test struct {
	Test string
}

var Models = map[string]interface{}{
	"test": newTest(),
}

func main() {
	test1 := Test{}
	fmt.Println("Test 1: ")
	fmt.Printf("%v", test1)
	fmt.Println()
	fmt.Println("Test 1 as pointer: ")
	fmt.Printf("%v", &test1)
	fmt.Println()
	test2 := Models["test"]
	fmt.Println("Test 2: ")
	fmt.Printf("%v", test2)
	fmt.Println()
	fmt.Println("Test 2 as pointer: ")
	fmt.Printf("%v", &test2)
	
}

func newTest() Test {
	var model Test 
	return model
}
英文:

There are 2 kinds of variables that I have. Check for the Go playground, and I don't understand why this is happening. The problem: what I get from the Models it should be a struct to use it for GORM First() function.

The code:

package main

import (
	"fmt"
)

type Test struct {
	Test string
}

var Models = map[string]interface{}{
	"test": newTest(),
}

func main() {
	test1 := Test{}
	fmt.Println("Test 1: ")
	fmt.Printf("%v", test1)
	fmt.Println()
	fmt.Println("Test 1 as pointer: ")
	fmt.Printf("%v", &test1)
	fmt.Println()
	test2 := Models["test"]
	fmt.Println("Test 2: ")
	fmt.Printf("%v", test2)
	fmt.Println()
	fmt.Println("Test 2 as pointer: ")
	fmt.Printf("%v", &test2)
	
}

func newTest() Test {
	var model Test 
	return model
}

答案1

得分: 1

根据golang文档中的大小和对齐保证

如果结构体或数组类型不包含任何大小大于零的字段(或元素),则其大小为零。两个不同的零大小变量在内存中可能具有相同的地址。

这就是为什么Test 1&{}

接口值表示为一个包含两个字的对,其中一个字是指向存储在接口中的类型信息的指针,另一个字是指向关联数据的指针。

Test2是一个interface{},因此Test2具有指向存储类型信息的指针和指向数据本身的指针。它包含类型和值信息。

英文:

According to the Size and alignment guarantees as per golang docs

> A struct or array type has size zero if it contains no fields (or
> elements, respectively) that have a size greater than zero. Two
> distinct zero-size variables may have the same address in memory.

This is why Test 1 is &{}

> Interface values are represented as a two-word pair giving a pointer
> to information about the type stored in the interface and a pointer to
> the associated data.

Test2 is an interface{}, Test2 thus has pointer to information about type stored and pointer to the data itself. It Type and Value informations

答案2

得分: 1

TL;DR: 在第一个例子中,你传递的是类型为*Test的值进行打印,但在第二个例子中,你传递的是类型为*interface{}的值!%v表示使用默认格式进行格式化,但默认格式取决于值的类型。


你看到的差异只是fmt包实现的默认格式化规则。

你正在使用fmt.Printf()

func Printf(format string, a ...interface{}) (n int, err error)

它接受一个格式化字符串和其他类型为interface{}的参数。因此请注意,如果你传递的值不是interface{}类型,该值将被包装在一个interface{}类型的值中。

现在让我们看看你的例子:

test1 := Test{}
// ...
fmt.Printf("%v", &test1)

test1的类型是Test,你传递的是类型为*Test&test1。这将被包装在一个interface{}中。fmt包文档中的格式化规则:

> 对于复合对象,元素将按照这些规则递归地打印,排列如下:
>
> struct: {field0 field1 ...}
> array, slice: [elem0 elem1 ...]
> maps: map[key1:value1 key2:value2]
> pointer to above: &{}, &[], &map[]

由于它是一个指向struct的指针,将使用&{}格式。Test有一个字段Test string,但你没有设置它的值,所以它默认为类型string零值,即空字符串""。这就是为什么显示时什么都看不到。请注意,如果你像这样初始化它:

test1 := Test{"a"}

输出将是:

&{a}

让我们看看你的第二个例子:

test2 := Models["test"]
// ...
fmt.Printf("%v", &test2)

第一行是一个短变量声明test2的类型将从右侧表达式推断出来。右侧表达式是一个索引表达式,对一个映射进行索引。它的类型将是映射的值类型,由于Models的类型是map[string]interface{}test2的类型将是interface{}

到目前为止还好。但是当你尝试像这样打印它fmt.Printf("%v", &test2)时会发生什么?你传递的是指向test2的指针,它的类型是interface{},所以你传递的是类型为*interface{}的值,由于这与interface{}不相同,它将被包装在另一个interface{}值中。

因此,传递给fmt.Printf()的是一个interface{}值,它包装了一个*interface{}值,该值是test2变量的地址。

现在适用的格式化规则是:

> %v的默认格式为:
>
> bool: %t
> int, int8等: %d
> uint, uint8等: %d,%x(如果使用%#v打印)
> float32, complex64等: %g
> string: %s
> chan: %p
> pointer: %p

由于要格式化的值是一个指针(*interface{}),%v将默认为%p,即:

> 指针:
>
> %p 以16进制表示,带有前导0x

因此,结果是以十六进制格式正确打印地址值,例如:

0x1040a160

要从test2获取一个结构体,你可以使用类型断言。所以它应该是这样的:

t2 := Models["test"]
test2 := t2.(Test) // test2的类型是Test

这个test2test1的类型相同,打印时会产生相同的结果。在Go Playground上试一试。

最好的方法是在映射中存储*Test值,这样就不需要类型断言甚至存储在本地变量中了,因为存储在映射中的interface{}已经是指向Test的指针,可以直接使用/传递。

英文:

TL;DR: In the first case you pass a value of type *Test for printing, but in the second case you pass a value of type *interface{}! The %v verb means to format using the default formatting, but the default formatting depends on the type of the value.


The difference you see is just the default formatting rules of the fmt package's implementation.

You're using fmt.Printf():

func Printf(format string, a ...interface{}) (n int, err error)

which takes a format string and other arguments as type interface{}. So note that if the value you pass is not of type interface{}, the value will be wrapped in a value of type interface{}.

Now let's see your examples:

test1 := Test{}
// ...
fmt.Printf("%v", &test1)

test1 is of type Test, and you pass &test1 which is of type *Test. This will be wrapped in an interface{}. The formatting rules from the package doc of fmt:

> For compound objects, the elements are printed using these rules, recursively, laid out like this:
>
> struct: {field0 field1 ...}
> array, slice: [elem0 elem1 ...]
> maps: map[key1:value1 key2:value2]
> pointer to above: &{}, &[], &map[]

Since it is a pointer to a struct, the &{} format will be used. Test has a field Test string, but you didn't set its value, so it defaults to the zero value of the type string which is the empty string "". That's why you see nothing when displayed. Note that if you would have initialized it like this:

test1 := Test{"a"}

The output would have been:

&{a}

Let's see your 2nd example:

test2 := Models["test"]
// ...
fmt.Printf("%v", &test2)

The first line is a short variable declaration, type of test2 will be inferred from the right-hand side expression. The right hand side expression is an index expression, indexing a map. Its type will be the value type of the map, and since type of Models is map[string]interface{}, type of test2 will be interface{}.

So far so good. But what happens when you try to print it like fmt.Printf("%v", &test2)? You pass a pointer to test2 which is of type interface{}, so what you pass is of type *interface{}, and since this is not identical to interface{}, it will be wrapped in another interface{} value.

So what gets passed to fmt.Printf() is an interface{} value, wrapping a *interface{} value being the address of the test2 variable.

And now the formatting rule that applies here:

> The default format for %v is:
>
> bool: %t
> int, int8 etc.: %d
> uint, uint8 etc.: %d, %x if printed with %#v
> float32, complex64, etc: %g
> string: %s
> chan: %p
> pointer: %p

Since the value to be formatted is a pointer (*interface{}), %v will default to %p, which is:

> Pointer:
>
> %p base 16 notation, with leading 0x

So the result is properly printing the address value in hexadecimal format, e.g.:

0x1040a160

To obtain a struct from test2, you can use type assertion. So it should rather be something like this:

t2 := Models["test"]
test2 := t2.(Test) // test2 is of type Test

This test2 has identical type to that of test1, and will produce the same result when printing. Try it on the Go Playground.

Best would be though to store *Test values in the map, and so no type assertion or even storing in local variable would be necessary, as the interface{} stored in the map would already be a pointer to Test, which can be used / passed around as-is.

huangapple
  • 本文由 发表于 2017年2月4日 19:29:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/42039827.html
匿名

发表评论

匿名网友

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

确定