声明一个结构体变量和将一个空结构体赋值给一个变量之间有什么区别?

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

What is the difference between declare a variable of struct VS assign a empty struct to a variable

问题

以下是翻译好的内容:

type person struct {
    age    int
    gender string
}

var tom person
var jim person = person{}

第一行中,tom 被声明为 person 类型,第二行中,jim 被声明为 person 类型并被赋予一个空的 person。这两种方式中的默认值都是相同的。在第一行中,一旦声明了 tom,它会分配内存存储空间吗?

与以下代码相同:

var i int

上面的代码是分配内存还是仅表示一个地址?

var i *int = new(int)

我知道这两段代码中的 i 变量有不同的含义,第一个是 int 类型的变量,第二个是指针,但是第一个 i 也应该表示内存中的某个地址,这意味着它将为 i 分配一个特定的地址,并将 "i" 默认为 0?

英文:
type person struct {
    age    int
    gender string
}
(1)var tom person
(2)var jim person = person{}

In line one,tom is declared as person,line 2,jim is declared as person and assigned an empty person to it,what is the difference between these two?The default value are the same in both ways.Will line 1 allocate memory storage once it is declared without assigning anything to it?

Same thing as

var i int

Does the code above allocate memory or just denote an address as doing this

var i *int = new(int)

I know the i variable has different meanings in these two code,the first is a variable of type int and the second is a pointer,but the first i should also represent some address in the memory which means it allocate a certain address to i and defaults "i" to 0?

答案1

得分: 2

声明变量与用零值初始化变量的区别

在Go语言中,所有没有初始化器的变量都会被初始化为其类型适当的所谓“零值”。对于结构体person来说,它的零值表达式是person{},所以情况(1)和(2)是一样的。我认为编译器会为它们生成相同的代码。

引用规范

变量声明创建一个变量,将标识符绑定到它,并给它一个类型和可选的初始值。

...

如果给出了一个表达式列表,变量将通过按顺序将表达式赋值给变量来进行初始化;所有表达式必须被消耗,并且所有变量都必须从它们进行初始化。否则,每个变量都将被初始化为其零值

通过声明变量进行内存分配

声明var i int会声明一个变量并对其进行初始化。从某种意义上说,这确实意味着分配内存(并对其进行初始化),但我不会深入研究这个问题:你只需要知道编译器会安排变量的存在即可。

至于它是否“表示一个地址”,这取决于你如何看待它:程序员不需要考虑变量在内存中的布局方式——变量的名称是对其值的句柄,编译器可以自由地以任何方式提供这样的访问。我的意思是,编译器可能在某个地方内部存储该变量的地址并使用它,但你不需要关心这个。

声明var i *int = new(int)会声明一个指向匿名变量的指针(一个具有特定类型语义的内存块),将该变量的内存初始化为适当的零值,然后将该内存块的地址赋值给你声明的变量。

引用规范

内置函数new接受一个类型T并返回一个类型为*T的值。内存的初始化如初始值一节所述。

在这种情况下,你操作的是一个指针,而不是变量本身。因此,这两个声明做的事情完全不同,它们声明了不同类型的变量,并且生成的变量具有不同的语义。

var i intvar p *int = new(int)之间可能最主要的区别在于,在前一种情况下,变量会立即(静态地)被创建,而在后一种情况下,匿名变量的内存会动态地进行初始化。但同样不要过于依赖这一点:当执行到var i int声明时,除了名为i、类型为int的变量可用之外,你无法确定程序中确切发生了什么。例如,编译器可以自由地使用“堆”来分配它,而不是“栈”(假设典型的硬件架构)。

英文:

Declaring a variable vs initializing it with zero value

In Go, all variables declared without initializers are initialized to the so-called zero values appropriate for their types. Since for a struct person its zero value is the expression person{} both cases, (1) and (2) do the same thing. I reckon the compiler will generate the same code for them.

To cite the spec:

> A variable declaration creates a variable, binds an identifier to it and gives it a type and optionally an initial value.
>
> …
>
> If a list of expressions is given, the variables are initialized by assigning the expressions to the variables in order; all expressions must be consumed and all variables initialized from them. Otherwise, each variable is initialized to its zero value.

Memory allocation by declaring a variable

The declaration var i int declares a variable and initializes it. In some sense, this does mean allocating memory (and initializing it) but I'd not dig that deep: you should be fine with the fact the compiler will arrange for the variable to exist.

As to whether that "denotes an address" or not depends on how you look at this: it's not the programmer's business to think about how the variable is laid out in memory—the name of the variable is a handle on its value, and the compiler is free to provide such access however it pleases. I mean, the compiler may internally store the address of this variable somewhere and use it but you should not be concerned with it.

The var i *int = new(int) declaration declares a pointer to an anonymous variable (a memory block which has the semantics of a certain type "attached" to it), initializes the memory of that variable with the zero value appropriate for its type and then assigns the address of that memory block to the variable you're declaring.

To cite the spec:

> The built-in function new takes a type T and returns a value of type *T. The memory is initialized as described in the section on initial values.

In this case, you operate on a pointer, not on a variable itself. So these two declarations do quite different things as they declare different types of variables, and the resulting variables have different semantics.

Possibly the chief difference between var i int and var p *int = new(int) lies in that in the former case the variable is created "right away" (statically) while in the latter case the memory for the anonymous variable is initialized dynamically. But again do not be too attached to this: when the execution hits the var i int declaration, you cannot know for sure what exactly happens in the program other than the variable named i of type int becomes available. For instance, the compiler is free to use "the heap" to allocate it, not "the stack" (assuming a typical H/W architecture).

答案2

得分: 0

尽管tomjim的定义具有相同的效果,但它们生成了不同的机器代码。我将这两个声明放入一个简单的函数中,使用go tool 6g -S foo.go进行编译,并得到了以下汇编代码:

0004 (foo.go:8) TYPE tom+-48(SP){"".person},$24
0005 (foo.go:8) TYPE jim+-24(SP){"".person},$24
0006 (foo.go:9) LEAQ tom+-48(SP),DI
0007 (foo.go:9) MOVQ $0,AX
0008 (foo.go:9) STOSQ ,
0009 (foo.go:9) STOSQ ,
0010 (foo.go:9) STOSQ ,
0011 (foo.go:10) LEAQ statictmp_0000+0(SB),BX
0012 (foo.go:10) LEAQ jim+-24(SP),BP
0013 (foo.go:10) MOVQ BP,DI
0014 (foo.go:10) MOVQ BX,SI
0015 (foo.go:10) MOVSQ ,
0016 (foo.go:10) MOVSQ ,
0017 (foo.go:10) MOVSQ ,

因此,栈上为每个变量分配了空间,但是tom通过将零存储到其内存中进行初始化,而jim通过从其他内存中复制进行初始化(可能是程序的只读数据段中的某些内容)。对于大多数情况,这可能并不重要:如果变量初始化是程序运行时间的主要部分,那么可能并没有太大影响。

但是,如果您有一个特别大的变量(例如一个大数组)并且希望将其初始化为零值,您可能会看到差异。

英文:

While the definitions of tom and jim have the same effect, they produce different machine code. I put the two declarations into a simple function, compiled it with go tool 6g -S foo.go, and got this assembly for the output for the two declarations:

0004 (foo.go:8) TYPE    tom+-48(SP){"".person},$24
0005 (foo.go:8) TYPE    jim+-24(SP){"".person},$24
0006 (foo.go:9) LEAQ    tom+-48(SP),DI
0007 (foo.go:9) MOVQ    $0,AX
0008 (foo.go:9) STOSQ   ,
0009 (foo.go:9) STOSQ   ,
0010 (foo.go:9) STOSQ   ,
0011 (foo.go:10) LEAQ    statictmp_0000+0(SB),BX
0012 (foo.go:10) LEAQ    jim+-24(SP),BP
0013 (foo.go:10) MOVQ    BP,DI
0014 (foo.go:10) MOVQ    BX,SI
0015 (foo.go:10) MOVSQ   ,
0016 (foo.go:10) MOVSQ   ,
0017 (foo.go:10) MOVSQ   ,

So space on the stack has been allocated on the stack for each variable, but tom is initialised by storing zeroes to its memory while jim is initialised by copying from some other memory (probably something in the read only data segment of the program). For most cases, this is probably not going to matter: if variable initialisation is a large part of your program runtime then you probably aren't doing much.

But if you have a particularly large variable (e.g. a large array) and want it initialised to its zero value, you might start to see a difference.

huangapple
  • 本文由 发表于 2014年4月17日 23:03:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/23136725.html
匿名

发表评论

匿名网友

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

确定