【问题标题】:Difference between golang pointersgolang指针的区别
【发布时间】:2017-06-21 17:52:06
【问题描述】:

我有两种变量。检查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
}

【问题讨论】:

  • 您应该在此处粘贴代码。简而言之,test1 的类型为 Testtest2 的类型为 interface{}(也称为空接口)。这是一种不同的类型。
  • @ymonad 是的,我检查过了,但我怎样才能返回正确的结构?
  • 当然你可以把map[string]interface{}改成map[string]Test。但是我想这是XY problem。你有什么具体的问题想解决吗?
  • @ymonad 模型数组充满了结构,我总是想返回正确结构的实例。例如,有 300 个结构都代表数据库表,我总是想返回我想要使用的表的实例。

标签: pointers go struct interface formatting


【解决方案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,而您传递的 &test1 的类型为 *Test。这将包含在interface{} 中。 fmt的包文档中的格式化规则:

对于复合对象,使用这些规则以递归方式打印元素,布局如下:

struct:             {field0 field1 ...}
array, slice:       [elem0 elem1 ...]
maps:               map[key1:value1 key2:value2]
pointer to above:   &{}, &[], &map[]

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

test1 := Test{"a"}

输出应该是:

&{a}

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

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

第一行是short variable declarationtest2 的类型将从右侧表达式中推断出来。右侧表达式是index expression,索引地图。它的类型将是映射的值类型,由于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 etc.:          %d
uint, uint8 etc.:        %d, %x if printed with %#v
float32, complex64, etc: %g
string:                  %s
chan:                    %p
pointer:                 %p

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

指针:

%p    base 16 notation, with leading 0x

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

0x1040a160

要从test2 获取结构,可以使用type assertion。所以它应该是这样的:

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

test2test1 具有相同的类型,并且在打印时将产生相同的结果。在Go Playground 上试试吧。

最好将*Test 值存储在映射中,因此不需要类型断言甚至存储在局部变量中,因为存储在映射中的interface{} 已经是指向Test 的指针,可以按原样使用/传递。

【讨论】:

  • 不错的答案,主要是我发现了这一点,但目前我没有解决我的问题:我在上面写了我的问题,你的建议是什么? “模型数组充满了结构,我总是想返回正确结构的实例。例如,有 300 个结构都代表数据库表,我总是想返回我想要的表的实例使用”
  • @PumpkinSeed 我不明白你的例子,也许你应该把它作为一个新问题发布?
  • 我接了,希望一切可以理解:stackoverflow.com/questions/42038782/…
【解决方案2】:

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

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

这就是为什么Test 1&{}

接口值表示为两个单词对,给出一个指针 有关存储在接口中的类型的信息和指向的指针 相关数据。

Test2interface{},因此 Test2 具有指向存储类型信息的指针和指向数据本身的指针。它的类型和值信息

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-27
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    相关资源
    最近更新 更多