Go语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

结构体定义需要使用type和struct语句。struct语句定义一个新的数据类型,结构体中有一个或多个成员。
type语句设定了结构体的名称。格式如下:

type struct_name  struct {
name string
talk Talk
}

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:

variable_name := structure_name{value1,value2}  //顺序必须与结构体的定义一致

或者:

variable_name := structure_name{key1:value1,key2:value2}  //结构体名称:值

结构体类型中的每个字段都需要独占一行。一般情况下,字段声明需由字段名称和表示字段类型的字面量组成。
还有一种只有类型字面量的无名称字段,称为嵌入字段。
虽然嵌入字段可以用来无缝集成额外字段和方法,但是其嵌入规则和使用规则都比较复杂。

结构体类型的值一般由复合字面量来表达。
复合字面量可以由类型字面量和花括号包裹的键值对列表组成。
这里,键就是结构体类型中某个字段的名称,而值(又称元素)就是要赋给该字段的那个值。
表示结构体值的复合字面量可以简称为结构体字面量。
在同一个结构体字面量中,一个字段名称只能出现一次。

字段名必须唯一,可用"_"补位,支持使用自身指针类型成员.
字段名,排列顺序属于类型组成部分.除对齐处理外,编译器不会优化和调整内存布局。

package main

import "fmt"

type node struct {
_ int //没给值会使用默认值
id int
next *node
} func main() {
n1 := node{
id: 1,
} n2 := node{
id: 2,
next: &n1,
} fmt.Println(n1, n2) //{0 1 <nil>} {0 2 0xc000048400}
}

可按顺序初始化全部字段,或使用命名方式初始化指定字段。

package main

import "fmt"

func main() {
type user struct {
name string
age int
} u1 := user{"Tom", 12} //如果顺序初始化字段,就必须赋值全部字段
u2 := user{"Kebi"} //too few values in user literal,字段数量不够
u3 := user{ //命名初始化
name: "maoixan",
age: 18,
} fmt.Println(u1, u3)
}

推荐使用命名初始化,这样在扩充结构字段或调整字段顺序时,不会导致语句初始化错误.
可以直接匿名结构类型变量,还可以将结构体用作字段类型。

package main

import "fmt"

func main() {
u := struct { //直接定义匿名结构变量
name string
age int8
}{
name: "kebi",
age: 18,
} type file struct {
name string
attr struct { //定义匿名结构类型字段
owner int
perm int
}
} f := file{
name: "test.py",
// attr: { //missing type in composite literal,对于结构体中的结构体赋值方式有所不同
// owner: 10,
// perm: 755,
// },
}
f.attr.owner = 10 //正确方式
f.attr.perm = 755 fmt.Println(u, f) //{kebi 18} {test.py {10 755}}
}

只有在所有字段类型全部支持时,才可做相等操作。

package main

import "fmt"

func main() {
type data struct {
x int
y map[string]int //字典类型不支持==,
} d1 := data{
x: 100,
} d2 := data{
x: 100,
}
fmt.Println(d1 == d2) //struct containing map[string]int cannot be compared
}

可使用指针直接操作结构字段,但不能是多级指针。

package main

import "fmt"

func main() {
type user struct {
name string
age int
} p := &user{ //获取指针
name: "kebi",
age: 26,
} p.name = "maoxian" //通过指针找到对应的程序实体
p.age++
fmt.Println(p) //&{maoxian 27} p2 := &p //&p属于二级指针
*p2.name = "xiaoniao" //p2.name undefined (type **user has no field or method name)
}

空结构

空结构struct{}是指没有字段的结构类型。
它比较特殊,因为无论是其自身,还是作为数组元素类型,其长度都为0。

package main

import (
"fmt"
"unsafe"
) func main() {
var a struct{} //匿名结构体
var b [100]struct{} //以结构体作为元素类型的数组 fmt.Println(unsafe.Sizeof(a), unsafe.Sizeof(b)) //0 0 s := b[:]
b[1] = struct{}{} //重新赋值
s[2] = struct{}{}
fmt.Println(s[3], len(s), cap(s)) //{} 100 100
}

实际上,这类长度为0的对象通常指向runtime.zerobase变量。

package main

import "fmt"

func main() {
a := [10]struct{}{}
b := a[:]
c := [0]int{} fmt.Printf("%p, %p, %p\n", &a[0], &b[0], &c) //0x5771c8, 0x5771c8, 0x5771c8
}

空结构可作为通道元素类型,用于事件通知。

package main

import "fmt"

func main() {
exit := make(chan struct{}) go func() {
fmt.Println("hello, world!")
exit <- struct{}{}
}() <-exit
fmt.Println("end.")
}

匿名字段
所谓匿名字段是指没有名字,仅有类型的字段,也称作嵌入字段或嵌入类型。
从编译器角度看,这只是隐式地以类型名作为字段名称。
可直接引用匿名字段的成员,但初始化时必须当作独立字段。

package main

import "fmt"

type attr struct {
perm int
} type file struct {
name string
attr //仅有类型名
} func main() {
f := file{
name: "test.dat",
attr: attr{ //将类型名当作字段名
perm: 755,
},
}
f.perm = 500 //直接设置匿名字段成员
fmt.Println(f, f.perm) //直接读取匿名字段成员
}

如果嵌入其它包中的类型,则隐式字段名称不包括包名。
不仅仅是结构体,除接口指针和多级指针以外的任何命名类型都可以作为匿名字段。

package main

import "fmt"

type data struct {
*int //嵌入指针类型
string
} func main() {
x := 100
d := data{
int: &x, //使用基础类型作为字段名
string: "abc",
} fmt.Printf("%#v\n", d)
}
/*
main.data{
int:(*int)(0xc00000a168),
string:"abc"
}
*/ 不能将基础类型和其指针类型同时嵌入,因为两者隐式名字相同,下面就是错误示例。
// type data struct {
// *int
// int
// }

虽然可以像普通字段那样访问匿名字段成员,但会存在重名问题。
默认情况下,编译器从当前显式命名开始,逐步向内查找匿名字段成员。
如果匿名字段成员被外层同名字段遮蔽,那么必须使用显式字段名。

package main

import "fmt"

type file struct {
name string
} type data struct {
file
name string //与匿名字段file.name重名
} func main() {
d := data{
name: "data",
file: file{"file"}, //这种方式赋值并没有影响
} fmt.Println(d.name, d.file.name) //data file
d.name = "data2"
d.file.name = "file2" fmt.Println(d.name, d.file.name) //data2 file2
}

如果多个相同层级的匿名字段成员重名,就只能使用显式字段名访问,因为编译器无法确定目标。

package main

import "fmt"

type file struct {
name string
} type log struct {
name string
} type data struct {
file //file和log层次相同
log //file.name和log.name重名
} func main() {
d := data{
file: file{"1.txt"},
log: log{"test.log"},
}
fmt.Println(d) //{{1.txt} {test.log}} d2 := data{}
// d2.name = "name" //ambiguous selector d2.name
d2.file.name = "file" //显式命名字段
d2.log.name = "log"
fmt.Println(d2) //{{file} {log}}
}

严格说来,Go并不是传统意义上的面向对象编程语言,或者说仅实现了最小面向对象的机制。
匿名嵌入不是继承,无法实现多态处理。
虽然配合方法集,可用接口来显现一些类似的操作,但其本质完全不同。

字段标签

字段标签(tag)并不是注释,而是用来对字段进行描述的元数据。
尽管它不属于数据成员,但却是类型的组成部分。
在运行期,可以反射获取标签信息。常被用作格式校验,数据库关系映射等。

package main

import (
"fmt"
"reflect"
) type user struct {
name string `昵称`
sex int `性别`
} func main() {
u := user{"Tom", 1}
v := reflect.ValueOf(u)
t := v.Type() for i, n := 0, t.NumField(); i < n; i++ {
fmt.Printf("%s: %v\n", t.Field(i).Tag, v.Field(i))
}
} /*
昵称: Tom
性别: 1
*/

  

go——结构的更多相关文章

  1. 【.net 深呼吸】细说CodeDom(1):结构大观

    CodeDom 是啥东东?Html Dom听过吧,XML Dom听过吧.DOM一般可翻译为 文档对象模型,那 Code + DOM呢,自然是指代码文档模型了.如果你从来没接触过 CodeDom,你大概 ...

  2. 读书笔记:《HTML5开发手册》--HTML5新的结构元素

    读书笔记:<HTML5开发手册> (HTML5 Developer's CookBook) 虽然从事前端开发已有很长一段时间,对HTML5标签也有使用,但在语义化上面理解还不够清晰.之前在 ...

  3. Python学习--04条件控制与循环结构

    Python学习--04条件控制与循环结构 条件控制 在Python程序中,用if语句实现条件控制. 语法格式: if <条件判断1>: <执行1> elif <条件判断 ...

  4. HTML5 语义元素(一)页面结构

    本篇主要介绍HTML5增加的语义元素中关于页面结构方面的,包含: <article>.<aside>.<figure>.<figcaption>.< ...

  5. React在开发中的常用结构以及功能详解

    一.React什么算法,什么虚拟DOM,什么核心内容网上一大堆,请自行google. 但是能把算法说清楚,虚拟DOM说清楚的聊聊无几.对开发又没卵用,还不如来点干货看看咋用. 二.结构如下: impo ...

  6. ElasticSearch 5学习(10)——结构化查询(包括新特性)

    之前我们所有的查询都属于命令行查询,但是不利于复杂的查询,而且一般在项目开发中不使用命令行查询方式,只有在调试测试时使用简单命令行查询,但是,如果想要善用搜索,我们必须使用请求体查询(request ...

  7. ASP.NET Core应用针对静态文件请求的处理[4]: DirectoryBrowserMiddleware中间件如何呈现目录结构

    和StaticFileMiddleware中间件一样,DirectoryBrowserMiddleware中间本质上还是定义了一个请求地址与某个物理目录之间的映射关系,而目标目录体现为一个FilePr ...

  8. Go结构体实现类似成员函数机制

    Go语言结构体成员能否是函数,从而实现类似类的成员函数的机制呢?答案是肯定的. package main import "fmt" type stru struct { testf ...

  9. CRL快速开发框架系列教程十(导出对象结构)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  10. Javacript实现字典结构

    字典是一种用[键,值]形式存储元素的数据结构.也称作映射,ECMAScript6中,原生用Map实现了字典结构. 下面代码是尝试用JS的Object对象来模拟实现一个字典结构. <script& ...

随机推荐

  1. JVM Specification 9th Edition (4) Chapter 4. The class File Format

    Chapter 4. The class File Format Table of Contents 4.1. The ClassFile Structure 4.2. Names 4.2.1. Bi ...

  2. 国内外知名IT科技博客(强烈推荐)

    国内 1.36氪(www.36kr.com): 目前国内做的最风生水起的科技博客,以介绍国内外互联网创业新闻为主的博客网站,自己建立有36Tree互联网创业融投资社区.36氪的名字源于元素周期 表的第 ...

  3. 【Mac + Appium + Python3.6学习(四)】之常用的IOS自动化测试API总结

    环境: Appium:1.9.1 Appium-desktop:1.7.1 Xcode:10.0 IOS:iPhone5S(10.3.3) Mac:10.13.6 ①获取手机屏幕size:长.宽 &l ...

  4. openldap+php-ldap操作

    一.基础知识首先,如果您对LDAP 不认识,建议先看看[原]LDAP服务介绍一文.本文以Linux 下常用的OpenLDAP为例说明.LDAP 以数方式存放数据,每个节点可存放属性或作为下面节点的父节 ...

  5. socket文件权限变更引起crs无法启动故障

    Crs无法正常启动,也无法关闭.[root@rac101 ~]# crsctl stop crsStopping resources. This could take several minutes. ...

  6. iOS 横向菜单

    MKHorizMenu 源码地址 现在想要实现以下效果,其中“选时间”这部分是一个NavigationBar,“日期”是横向的菜单,“电影时段”是TableView. 比较难实现的是横向菜单,因为没有 ...

  7. MVC [Control与View交互]

    <1> Home控制器 using System; using System.Collections.Generic; using System.Data; using System.Da ...

  8. 【BZOJ4439】[Swerc2015]Landscaping 最小割

    [BZOJ4439][Swerc2015]Landscaping Description FJ有一块N*M的矩形田地,有两种地形高地(用‘#’表示)和低地(用‘.’表示) FJ需要对每一行田地从左到右 ...

  9. 160620、利用 jQuery UI 和 Ajax 创建可定制的 Web 界面

    如今,网站比以往更具可定制性,允许用户更改其空间,根据自己的喜好对其进行个性化.个性化的主页或仪表板页面(例如 iGoogle.MyYahoo! 和 MyAOL)日渐普及,大多数 Web 应用程序内甚 ...

  10. delphi xe----操作mongoDB驱动,TMongoWire(Delphi MongoDB Driver)

    所有例子来自:https://github.com/stijnsanders/TMongoWire Delphi MongoDB的驱动 一个Delphi的驱动程序来访问mongoDB的服务器.用jso ...