本文介绍: type T struct {a, b int} 也是合法的语法,它更适用于简单结构体。结构体里的字段都有 名字,像 field1、field2 等,如果字段代码中从来也不会被用到,那么可以命名它为 _结构体的字段可以是任何类型,甚至是结构体本身,也可以是函数或者接口数组可以看作是一种结构类型,不过它使用下标而不是具名的字段。

结构


字段/属性 注意事项细节说明

1)字段声明语法同变量示例字段名字段类型

2)字段的类型可以为:基本类型数组引用类型

3)在创建一个结构变量后,如果没有给字段赋值,都对应一个零值(默认值)规则前面讲的一样:

布尔类型false数值是0,字符串是””。

数组类型的默认值和它的元素类型相关比如score [3]int则为[0,0,0]指针slicemap的零值都是nil,即还没有分配空间。(在使用时候使用make才可以给其赋值使用切片 map必须先make才能使用

type student struct {
	Name  string
	Age   int
	Score [5]float32
	ptr   *int
	slice []int
	map1  map[string]string
}

func main() {
	var s student
	fmt.Printf("%#v", s)

	//错误写法
	//s.slice[0] = 0
	//再次说明,使用切片时候,一定要使用make
	s.slice = make([]int, 10)

	//使用map时候一定要先make,错误写法s.map1["key1"] = "key1"
	s.map1 = make(map[string]string, 5)
	s.map1["key1"] = "value1"
}

如果在使用引用类型的时候要先使用make分配空间 

(4)不同结构变量的字段是独立互不影响一个结构变量字段的更改,不影响另外一个

	s1 := s
	s1.Name = "xxx"

结构体类型是值类型。

当结构体里面属性引用类型,那么第一件事情就是使用make,要不然代码崩溃。结构体这种数据类型默认是值拷贝,也就是值类型。

结构体定义


结构体定义的一般方式如下

type T struct {a, b int} 也是合法的语法,它更适用于简单的结构体。

结构体里的字段都有 名字,像 field1、field2 等,如果字段在代码中从来也不会被用到,那么可以命名它为 _

结构体的字段可以是任何类型,甚至是结构体本身,也可以是函数或者接口。可以声明结构体类型的一个变量然后像下面这样给它的字段赋值

数组可以看作是一种结构体类型,不过它使用下标而不是具名的字段。

初始化结构体 new(Type) = &Type{}


使用 new 函数一个新的结构体变量分配内存,它返回指向分配内存指针var t *T = new(T),如果需要可以把这条语句放在不同的行(比如定义是包范围的,但是分配没有必要在开始就做)。

输出

使用 fmt.Println 打印一个结构体的默认输出可以很好的显示它的内容,类似使用 %v 选项

就像在面向对象语言所作的那样,可以使用点号符给字段赋值:structname.fieldname = value

同样的,使用点号符可以获取结构体字段的值:structname.fieldname

在Go语言中这叫选择器selector)。无论变量一个结构体类型还是一个结构体类型指针,都使用同样的选择器符(selectornotation) 来引用结构体的字段:

初始化一个结构体实例(一个结构体字面量:structliteral)的更简短和惯用的方式如下:

或者:

混合字面量语法(composite literal syntax&struct1{a, b, c} 是一种简写底层仍然会调用 new ()这里值的顺序必须按照字段顺序来写。在下面的例子中能看到可以通过在值的前面放上字段名来初始化字段的方式表达式 new(Type) 和 &Type{} 是等价的。

时间间隔(开始和结束时间以秒为单位)是使用结构体的一个典型例子

初始化方式

在(A)中,值必须以字段在结构体定义时的顺序给出,& 不是必须的。(B)显示了另一种方式,字段名加一个冒号放在值的前面,这种情况下值的顺序不必一致,并且某些字段还可以被忽略掉,就像(C)中那样。

结构体类型和字段的命名遵循可见性规则,一个导出的结构体类型中有些字段是导出的,另一些不是,这是可能的。下图说明了结构体类型实例和一个指向它的指针内存布局

使用 new 初始化:

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

类型 struct1 在定义它的包 pack1 中必须是唯一的,它的完全类型名是:pack1.struct1。

下面的例子显示了一个结构体 Person,一个方法方法有一个类型为 *Person参数(因此对象本身是可以被改变的),以及三种调用这个方法不同方式:

输出:

在上面例子的第二种情况中,可以直接通过指针,像 pers2.lastName=”Woodward” 这样给结构体字段赋值没有像 C++ 中那样需要使用 -> 操作符,Go 会自动做这样的转换

注意:也可以通过解指针的方式来设置值:(*pers2).lastName = “Woodward”

结构体的内存布局


Go 语言中,结构体和它所包含数据内存中是以连续块的形式存在,即使结构体中嵌套有其他的结构体,这在性能上带来了很大的优势。不像 Java 中的引用类型,一个对象和它里面包含的对象可能会在不同内存空间中,这点和 Go 语言中的指针很像。下面的例子清晰地说明了这些情况:

递归结构体

结构体类型可以通过引用自身来定义。这在定义链表二叉树元素(通常叫节点)时特别有用,此时节点包含指向临近节点链接地址)。

如下所示链表中的 su,树中的 ri 和 le 分别是指向别的节点的指针。

链表

这块的 data 字段用于存放有效数据比如 float64),su 指针指向后继节点

Go 代码:

链表中的第一个元素head,它指向第二个元素最后一个元素tail,它没有后继元素,所以它的 sunil 值。当然真实的链接会有很多数据节点,并且链表可以动态增长或收缩。

同样地可以定义一个双向链表,它有一个前趋节点 pr 和一个后继节点 su:

二叉树

结构体转换

Go 中的类型转换遵循严格规则。当为结构体定义了一个 alias 类型时,此结构体类型和它的 alias 类型都有相同底层类型,它们可以如示例 10.3 那样互相转换,同时需要注意其中非法赋值或转换引起的编译错误

示例 10.3:

输出:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注