- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
虽然 Go 语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念,但是可以通过结构体的内嵌,再配合接口,来实现面向对象,甚至具有更高的扩展性和灵活性。那么本文就将详细看下怎么使用结构体.
Go 语言中的基础数据类型可以表示一些事物的基本属性,但是当想要表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了.
Go 语言提供了一种 自定义数据类型 , 可以封装多个基本数据类型 ,这种数据类型叫结构体 struct。通过 struct 可以封装自己所需的各种复杂类型.
在详解结构体之前,需要先了解两个概念,到底什么叫做自定义类型和类型别名?
【自定义类型】 。
在 Go 语言中有一些基本的数据类型,如 string、整型、浮点型、布尔等数据类型,Go 语言中可以使用 type 关键字来定义自定义类型.
自定义类型是定义了一个全新的类型。 我们可以基于内置的基本类型定义,也可以通过 struct 定义.
type MyInt int // 新定义一个类型 MyInt,以 int 类型为参照
通过 type 关键字的定义,MyInt 就是一种新的类型,它具有 int 的特性.
【类型别名】 。
类型别名 是 Go1.9 版本添加的新功能.
类型别名规定:TypeAlias 只是 Type 的别名,本质上 TypeAlias 与 Type 是同一个类型。就像一个小孩子有自己的小名和户口本的大名,都是指的同一个人.
type TypeAlias = Type
例如长江的 rune 和 byte 就是类型别名,他们的定义如下:
type byte = uint8
type rune = int32
【区别】 。
那么它们之前的 区别 是啥呢?下面分别定义一个变量看下它们的类型:
package main
import "fmt"
// 自定义类型
type NewInt int
// 类型别名
type MyInt = int
func main() {
var a NewInt
var b MyInt
fmt.Printf("NewInt type of a : %T\n", a)
fmt.Printf("MyInt type of b : %T\n", b)
}
有输出结果可知,自定义类型的 type 将变成新的名字 NewInt;类型别名 MyInt,则仍属于基本的 int 类型.
使用 type 和 struct 关键字来定义结构体.
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
// 类型名:标识自定义结构体的名称,在同一个包内不能重复
// 字段名:表示结构体字段名,结构体中的字段名必须唯一
// 字段类型:表示结构体字段的具体类型
举个例子,我们定义一个 Person(人)结构体:
type Person struct {
name string
city string
age int8
}
// 类型相同的字段可以放到一起,如下:
type Person struct {
name, city string
age int8
}
这样我们就拥有了一个 Person 的自定义类型,它有 name、city、age 三个字段,分别表示姓名、城市和年龄。这样我们使用这个 person 结构体就能够很方便的在程序中表示和存储人信息了。 。
语言内置的基础数据类型是用来描述一个值的,而 结构体是用来描述一组值 的。比如一个人有名字、年龄和居住城市等,本质上是一种聚合型的数据类型.
只有当结构体实例化时,才会真正地分配内存。也就是 必须实例化后才能使用结构体的字段 .
// 结构体实例化格式:
var 结构体实例名 结构体类型
下面是一个 Person 的示例:
package main
import "fmt"
type Person struct {
name string
city string
age int8
}
func main() {
var person Person
person.name = "中国"
person.city = "北京"
person.age = 18
fmt.Printf("person.name = %v\n", person.name)
fmt.Printf("person %%v = %v\n", person)
fmt.Printf("person %%#v = %#v\n", person)
}
结构体还可以直接用于临时的或仅使用一次的场景中,不用声明结构体的名称,例如 。
package main
import (
"fmt"
)
func main() {
var user struct {
Name string
Age int
}
user.Name = "张三"
user.Age = 18
fmt.Printf("%#v\n", user)
}
通过匿名结构体声明得来的实例 user.
当通过使用 new 关键字对结构体进行实例化后,得到的是 结构体的地址 .
另外,Go 语言中支持对结构体指针直接使用‘.’来访问结构体的成员。如下示例:
package main
import (
"fmt"
)
type Person struct {
name string
city string
age int8
}
func main() {
var p = new(Person) // 声明结构体指针
// p3 := &Person{} // 还可以通过 & 对结构体进行取地址操作,相当于对该结构体类型进行了一次 new 实例化操作
p.name = "测试"
p.age = 18
p.city = "北京"
fmt.Printf("%T\n", p) // 打印出类型
fmt.Printf("p = %#v\n", p)
}
【声明即初始化】 。
此时结构体的值均为默认零值:
package main
import (
"fmt"
)
type Person struct {
name string
city string
age int8
}
func main() {
// 声明即初始化
var p = new(Person)
fmt.Printf("p = %#v\n", p)
}
【使用键值对初始化】 。
当某些字段没有初始值的时候,该字段可以不写。此时,没有指定初始值的字段的值就是该字段类型的零值.
// 使用键值对初始化
p1 := Person{
name: "张三",
city: "北京",
age: 18,
}
fmt.Printf("p1 = %#v\n", p1)
// 对结构体指针进行键值对初始化
p2 := &Person{
name: "张三",
city: "北京",
age: 18,
}
fmt.Printf("p2 = %#v\n", p2)
【使用值的列表初始化】 。
// 使用值的列表初始化
p3 := &Person{
"张三",
"北京",
18,
}
fmt.Printf("p3 = %#v\n", p3)
注意,值列表需要满足以下要求:
package main
import (
"fmt"
)
type test struct {
a int8
b int8
c int8
d int8
}
func main() {
n := test{
1, 2, 3, 4,
}
fmt.Printf("n.a %p\n", &n.a)
fmt.Printf("n.b %p\n", &n.b)
fmt.Printf("n.c %p\n", &n.c)
fmt.Printf("n.d %p\n", &n.d)
}
Go 语言的结构体没有构造函数,但可以自己实现。例如,下方的代码就实现了一个 Person 的构造函数。 因为 struct 是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型.
package main
import (
"fmt"
)
type Person struct {
name string
city string
age int8
}
func newPerson(name, city string, age int8) *Person {
return &Person{
name: name,
city: city,
age: age,
}
}
func main() {
p9 := newPerson("张三", "测试", 90)
fmt.Printf("%#v\n", p9)
}
Go 语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的 this 或者 self.
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
// 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是 self、this 之类的命名
// 例如,Person 类型的接收者变量应该命名为 p,Connector 类型的接收者变量应该命名为 c 等
// 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型
// 方法名、参数列表、返回参数:具体格式与函数定义相同
下面是一个简单示例:
package main
import (
"fmt"
)
// Person 结构体
type Person struct {
name string
age int8
}
// NewPerson 构造函数
func NewPerson(name string, age int8) *Person {
return &Person{
name: name,
age: age,
}
}
// Dream Person 做梦的方法
func (p Person) Dream() {
fmt.Printf("%s 的梦想是学好 Go 语言!\n", p.name)
}
func main() {
p1 := NewPerson("张三同学", 25)
p1.Dream()
}
方法与函数的区别是, 函数不属于任何类型,方法属于特定的类型 .
【比较指针类型和值类型的接收者的区别】 。
指针类型 的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后, 修改都是有效的 .
这种方式就十分接近于其他语言中面向对象中的 this 或者 self。 例如我们为 Person 添加一个 SetAge 方法,来修改实例变量的年龄.
当方法作用于 值类型 接收者时,Go 语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但 修改操作只是针对副本,无法修改接收者变量本身 .
package main
import (
"fmt"
)
// Person 结构体
type Person struct {
name string
age int8
}
// NewPerson 构造函数
func NewPerson(name string, age int8) *Person {
return &Person{
name: name,
age: age,
}
}
// SetAge 设置p的年龄
// 使用指针接收者
func (p *Person) SetAge(newAge int8) {
p.age = newAge
}
// SetAge2 设置p的年龄
// 使用值接收者
func (p Person) SetAge2(newAge int8) {
p.age = newAge
}
func main() {
p1 := NewPerson("张三同学", 25)
fmt.Println("修改前的 age :", p1.age)
p1.SetAge(18)
fmt.Println("修改后的 age :", p1.age)
p1.SetAge2(20)
fmt.Println("修改后的 age :", p1.age)
}
使用指针类型的情况:
【任意类型都可以添加方法】 。
比如下面示例代码,给自定义类型添加方法:
package main
import (
"fmt"
)
// 自定义一个类型 MyInt,参考 int 类型
type MyInt int
// 为 MyInt 添加一个 SayHello 的方法
func (m MyInt) SayHello() {
fmt.Println("Hello, 我是一个 int。")
}
func main() {
var m1 MyInt
m1.SayHello()
m1 = 100
fmt.Printf("%#v %T\n", m1, m1)
}
结构体允许其成员字段在声明时没有字段名而只有类型,这种 没有名字的字段就称为匿名字段 .
package main
import (
"fmt"
)
// Person 结构体Person类型
type Person struct {
string
int
}
func main() {
p1 := Person{
"张三",
18,
}
fmt.Printf("%#v\n", p1)
fmt.Println(p1.string, p1.int)
}
匿名字段 默认采用类型名作为字段名 ,结构体要求字段名称必须唯一,因此一个结构体中 同种类型的匿名字段只能有一个 .
一个结构体中可以嵌套包含另一个结构体或结构体指针.
package main
import (
"fmt"
)
// Address 地址结构体
type Address struct {
Province string
City string
}
// User 用户结构体
type User struct {
Name string
Gender string
Address Address // 此处可以省略第二个 Address 以匿名类型方式
}
func main() {
user1 := User{
Name: "张三",
Gender: "女",
Address: Address{
Province: "北京",
City: "北京",
},
}
fmt.Printf("user1 = %#v\n", user1)
}
当嵌套结构体内部存在 相同的字段名 时,为了避免歧义 需要指定具体的内嵌结构体的字段 ,否则会提示异常.
package main
import (
"fmt"
)
// Animal 动物
type Animal struct {
name string
}
func (a *Animal) move() {
fmt.Printf("%s会动!\n", a.name)
}
// Dog 狗
type Dog struct {
Feet int8
*Animal // 通过嵌套匿名结构体实现继承
}
func (d *Dog) wang() {
fmt.Printf("%s会汪汪汪~\n", d.name)
}
func main() {
d1 := &Dog{
Feet: 4,
Animal: &Animal{ // 注意嵌套的是结构体指针
name: "乐乐",
},
}
d1.wang()
d1.move()
}
注意:结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问).
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成.
JSON 键值对是用来保存 JS 对象的一种方式,键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值;多个键值之间使用英文 , 分隔.
package main
import (
"encoding/json"
"fmt"
)
// Student 学生
type Student struct {
ID int
Gender string
Name string
}
// Class 班级
type Class struct {
Title string
Students []*Student
}
func main() {
c := &Class{
Title: "101",
Students: make([]*Student, 0, 200),
}
for i := 0; i < 2; i++ { // 创建 2 个学生对象
stu := &Student{
Name: fmt.Sprintf("stu%02d", i),
Gender: "男",
ID: i,
}
c.Students = append(c.Students, stu)
}
// JSON 序列化:结构体-->JSON 格式的字符串
data, err := json.Marshal(c)
if err != nil {
fmt.Println("json marshal failed")
return
}
fmt.Printf("json:%s\n\n", data)
// JSON 反序列化:JSON 格式的字符串-->结构体
str := `{"Title":"101","Students":[{"ID":0,"Gender":"男","Name":"stu00"},{"ID":1,"Gender":"男","Name":"stu01"}]}`
c1 := &Class{}
err = json.Unmarshal([]byte(str), c1)
if err != nil {
fmt.Println("json unmarshal failed!")
return
}
fmt.Printf("%#v\n", c1)
}
Tag 是结构体的元信息,可以在运行的时候通过反射的机制读取出来.
Tag 在结构体字段的后方定义,由 一对反引号包裹起来 ,具体的格式如下:
`key1:"value1" key2:"value2"`
结构体标签由 一个或多个键值对 组成。键与值使用冒号分隔,值用双引号括起来。 键值对之间使用一个空格分隔 .
注意事项:为结构体编写 Tag 时, 必须严格遵守键值对的规则 。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如,不要在 key 和 value 之间添加空格.
例如我们为 Student 结构体的每个字段定义 json 序列化时使用的 Tag:
package main
import (
"encoding/json"
"fmt"
)
// Student 学生
type Student struct {
ID int `json:"id"` // 通过指定 tag 实现 json 序列化该字段时的 key
Gender string // json 序列化是默认使用字段名作为 key
name string // 全小写字母,表示私有不能被 json 包访问
}
func main() {
s1 := Student{
ID: 1,
Gender: "女",
name: "张三",
}
data, err := json.Marshal(s1)
if err != nil {
fmt.Println("json marshal failed!")
return
}
fmt.Printf("json str:%s\n", data)
}
package main
import "fmt"
type student struct {
id int
name string
age int
}
func main() {
ce := make(map[int]student)
ce[1] = student{1, "张三", 22}
ce[2] = student{2, "李四", 23}
fmt.Println(ce)
delete(ce, 2)
fmt.Println(ce)
}
参考: http://www.topgoer.com/go%E5%9F%BA%E7%A1%80/%E7%BB%93%E6%9E%84%E4%BD%93.html 。
最后此篇关于struct结构体【GO基础】的文章就讲到这里了,如果你想了解更多关于struct结构体【GO基础】的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
这个问题已经有答案了: In C, does a pointer to a structure always point to its first member? (3 个回答) 已关闭 7 年前。
当执行第二个fscanf时,控制台停止工作。我做错了什么? 输入文件包含: 3 minsu 50 80 40 sarah 30 60 40 jason 70 80 90 代码: #define _CR
Swift 结构体是构建代码所用的一种通用且灵活的构造体。 我们可以为结构体定义属性(常量、变量)和添加方法,从而扩展结构体的功能。 与 C 和 Objective C 不同的是: 结构
我想在 javascript 中创建一个结构。我有一对信息,我想使用,例如: array[1] = new Struct(); array[1].name = "parameter-name"; ar
我不允许使用带有 in 关键字的结构,对吗?例如: struct Rect { float x,y,width,height; }; layout(location = 7) in Rect
我的结构声明的片段: struct record{ char type[4]; uint32_t data_size; uint32_t flags; uint32_t
您能否帮助我理解为什么我的 dataStruct 结构的值不是其成员之一的值? (对于simpleDataStruct结构) 我用这一行打印值: printf("dataStruct:........
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
这个问题已经有答案了: Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized point
我不确定如何在 C 中创建一个具有不同位大小的变量的结构,例如: 我想创建一个结构体,其中一个变量作为 8 位整数,一个变量作为 16 位 bool 值,一个变量作为 8 位 bool 值,一个变量作
我正在为一个项目编写一个通信协议(protocol),其中包括两个用于请求和响应的 C 结构。根据设备的设置方式,数据传输可以是请求(主模块)或响应(从模块)。 这些结构彼此非常接近。最大的区别在于请
我有以下 C 结构体,代表外部芯片中的寄存器 typedef union { // Individual Fields struct { uint8_t ELEM_1
我了解 C++,并且正在学习 C。我想将 nullptr 定义为 NULL,但是当我使用括号初始化结构时,它会导致错误并显示预期的“}”。我正在使用 Visual Studio 编译器,我的代码示例如
#include struct s { char *a1; int a; }; int main(){ struct s p={"asdv",11}; struct s p1=p;
如果将记录作为参数发送给函数,如何添加记录? struct record { char name[20]; int nr; }; void AddRecord(struct record **p_al
使用python向C语言的链接库传递数组、结构体、指针类型的数据 由于最近的项目频繁使用python调用同事的C语言代码,在调用过程中踩了很多坑,一点一点写出来供大家参考,我们仍然是使用ctype
枚举、结构体、类 注:本文为作者自己总结,过于基础的就不再赘述 ,都是亲自测试的结果。如有错误或者遗漏的地方,欢迎指正,一起学习。 1、枚举 枚举是用来定义一组通用类型的一组相关值 ,关键字enum
Swift 结构体是构建代码所用的一种通用且灵活的构造体 可以为结构体定义属性(常量、变量)和添加方法,从而扩展结构体的功能 与 C 和 Objective C 不同的是: 结构体不需要包
关于结构的快速问题: struct xint { int number; char string[12]; }; int main(int argc, char *argv[])
编辑 我发现了这个: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays 基本上,如果我有这样的东西: struc
我是一名优秀的程序员,十分优秀!