mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-12 03:55:28 +08:00
148 lines
4.4 KiB
Markdown
148 lines
4.4 KiB
Markdown
# 10.1 结构体定义
|
||
|
||
结构体定义的一般方式如下:
|
||
|
||
```go
|
||
type identifier struct {
|
||
field1 type1
|
||
field2 type2
|
||
...
|
||
}
|
||
```
|
||
|
||
` type T struct {a, b int}`也是合法的语法,它更适用于简单的结构体。
|
||
|
||
这个结构体里的成员都有名字(names),像field1,field2等,如果成员在代码从来也不会被用到,那么可以命名为*_*。
|
||
|
||
成员可以是任何类型,甚至是结构体本身(参考[10.5](10.5.md)),可以是函数或者接口(参考第11章)。可以定义结构体类型的一个变量,然后给它的成员像下面这样赋值:
|
||
|
||
```go
|
||
var s T
|
||
s.a = 5
|
||
s.b = 8
|
||
```
|
||
|
||
数组可以看作是一种结构体类型,不过它使用下标而不是命名的成员。
|
||
|
||
**使用new**
|
||
|
||
使用*new*函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:`var t *T = new(T)`,如果需要可以把这条语句放在不同的行(比如定义是包范围的,但是分配却没有必要在开始就做)。
|
||
|
||
```go
|
||
var t *T
|
||
t = new(T)
|
||
```
|
||
|
||
写这条语句的惯用法是:`t := new(T)`,变量*t*是一个指向`T`的指针,此时结构体成员的值是它们所属类型的零值。
|
||
|
||
声明`var t T`也会给`t`分配内存,并零值化内存,但是这个时候t是类型T。在这两种方式中,t通常被称做类型T的一个实例(instance)或对象(Object)。
|
||
|
||
[Listing 10.1—structs_fields.go](examples/chapter_10/structs_fields.go)给出了一个非常简单的例子:
|
||
|
||
```go
|
||
package main
|
||
import "fmt"
|
||
|
||
type struct1 struct {
|
||
i1 int
|
||
f1 float32
|
||
str string
|
||
}
|
||
|
||
func main {
|
||
ms := new(struct1)
|
||
ms.i1 = 10
|
||
ms.f1 = 15.5
|
||
ms.str= "Chris"
|
||
|
||
fmt.Printf("The int is: %d\n", ms.i1)
|
||
fmt.Printf("The float is: %f\n", ms.f1)
|
||
fmt.Printf("The string is: %s\n", ms.str)
|
||
fmt.Println(ms)
|
||
}
|
||
```
|
||
|
||
输出:
|
||
|
||
The int is: 10
|
||
The float is: 15.500000
|
||
The string is: Chris
|
||
&{10 15.5 Chris}
|
||
|
||
使用fmt.Println打印一个结构体的默认输出可以很好的显示它的内容,类似使用*%v*选项。
|
||
|
||
可以通过逗号符给成员赋不同的值,就像在面向对象语言中那样:` structname.fieldname = value `。
|
||
|
||
使用同样的逗号符可以获取结构体成员的值:` structname.fieldname `。
|
||
|
||
在Go中这叫*选择器(selector)*。无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的选择器符(selector-notation)来引用结构体的成员:
|
||
|
||
```go
|
||
type myStruct struct { i int }
|
||
var v myStruct // v has struct type
|
||
var p *myStruct // p is a pointer to a struct
|
||
v.i
|
||
p.i
|
||
```
|
||
|
||
初始化一个结构体实例(一个结构体字面量:struct-literal)的更简短和惯用的方式如下:
|
||
|
||
```go
|
||
ms := &struct1{10, 15.5, "Chris"}
|
||
// 此时ms的类型是 *struct1
|
||
```
|
||
|
||
或者:
|
||
|
||
```go
|
||
var mt struct1
|
||
ms := struct1{10, 15.5, "Chris"}
|
||
```
|
||
|
||
混合字面量语法(composite literal syntax) ` &struct1{a, b, c}` 是一种简写,底层仍然会调用` new ()`,这里值的顺序必须按照成员顺序来写。在下面的例子中能看到可以通过在值的前面放上成员名来初始化值。表达式` new(Type)` 和` &Type{}`是等价的。
|
||
|
||
结构体的典型例子是一个时间间隔(开始和结束时间以秒为单位):
|
||
|
||
```go
|
||
type Interval struct {
|
||
start int
|
||
end int
|
||
}
|
||
```
|
||
|
||
初始化方式:
|
||
|
||
```go
|
||
intr := Interval(0, 3) (A)
|
||
intr := Interval(end:5, start:1) (B)
|
||
intr := Interval(end:5) (C)
|
||
```
|
||
|
||
在(A)中值必须以成员在结构体定义时的顺序给出,*&*不是必须的。(B)显示了另一种方式,成员名加上一个冒号在值的前面,这种情况下值的顺序不必一致,并且某些成员还可以被忽略掉,就像(C)那样。
|
||
|
||
结构体类型和成员的命名遵循可见性规则([4.2](4.2.md)),一个导出的结构体类型中有些成员是导出的,另一些不是,这是可能的。
|
||
|
||
下图说明了结构体类型实例和一个指向它的指针的内存布局:
|
||
|
||
```go
|
||
type Point struct { x, y int }
|
||
```
|
||
|
||
使用new初始化:
|
||
|
||

|
||
|
||
作为结构体字面量初始化:
|
||
|
||

|
||
|
||
|
||
**练习9.2**
|
||
|
||
通过使用 unsafe 包中的方法来测试你电脑上一个整型变量占用多少个字节。
|
||
|
||
## 链接
|
||
- [目录](directory.md)
|
||
- 上一节:[10 结构(struct)与方法(method)](10.0.md)
|
||
- 下一节:[regexp 包](09.2.md)
|