1、struct
1.1.声明
type stu Student {
field1 类型
field2 类型
}
看一个例子
package main
type Student struct {
Name string
Age int
score float
}
func main() {
var stu Student
stu.Name="wang"
stu.Age=18
stu.score=80
fmt.Println(stu)
}
1.2.内存布局
struct的字段内存布局是连续的,可以打出每个字段的内存位置
package main
type Student struct {
Namr string
Age int
score float
}
func main() {
var stu Student
stu.Name="wang"
stu.Age=18
stu.score=80
fmt.Println(stu)
fmt.Printf("Name:%p\n",&stu.Name)
fmt.Printf("Name:%p\n",&stu.Age)
fmt.Printf("Name:%p\n",&stu.score)
}
编译执行
1.3.初始化
三种方式
- var stu Student
- var stu *Student = &Student{}
- var stu *Student = new(Student)
package main
// Student is a struct
type Student struct {
Name string
Age int
score float32
}
func main() {
// 方式一
var stu Student
stu.Name="wang"
stu.Age=18
stu.score=80
// 方式二
var stu1 *Student = &Student{
Name: "xi",
Age: 19,
}
// 方式二
var stu2 = Student{
Name: "teng",
Age: 90,
}
fmt.Println(stu.Name)
fmt.Println(stu1.Name)
fmt.Println(stu2.Name)
}
编译执行
1.4.链表
定义
type Student struct {
Name string
Next* Student
}
链表的每一节点都包含下一节点的地址,把所有的节点串联起来,第一个节点叫做链表头
节点插入
采用尾部插入法来实现多节点链表
package main
import "fmt"
type Student struct{
Name string
Age int
score float32
Next *Student
}
func Trans(p *Student) {
for p !- nil {
fmt.Println(*p)
p = p.Next
}
}
func main() {
var head Student
head.Name = "teng"
head.Age = 33
head.score = 100
var stu1 Student
stu1.Name = "shan"
stu1.Age = 30
stu1.score = 90
head.Next = &stu1
Trans(&head)
}
编译执行
上面的写法是一个节点一个节点插入的,每个节点的地址是已知的,所以可以使用循环来实现多节点插入
package main
import "fmt"
type Student struct{
Name string
Age int
score float32
Next *Student
}
func Trans(p *Student) {
for p != nil {
fmt.Println(*p)
p = p.Next
}
}
func InsertTail(q *Student) {
var tail = q
for i := 0;i < 10;i++ {
var stu *Student = &Student{
Name: fmt.sprintf("stu%d",i),
Age: rand.Intn(100),
score: rand.Float32() * 100,
}
tail.Next = stu
tail = stu
}
}
func main() {
var head Student
head.Name = "teng"
head.Age = 33
head.score = 100
InsertTail(&head)
Trans(&head)
}
编译执行
当然除了尾部插入实现多个链表输出,也可以使用头部插入来实现
package main
import "fmt"
type Student struct{
Name string
Age int
score float32
Next *Student
}
func Trans(p *Student) {
for p != nil {
fmt.Println(*p)
p = p.Next
}
}
func InsertHead(r *Student) *Student {
for i := 0;i < 10;i++ {
var stu *Student = &Student{
Name: fmt.sprintf("stu%d",i),
Age: rand.Intn(100),
score: rand.Float32() * 100,
}
stu.Next = r
r = stu
}
return r
}
func main() {
var head *Student = new(Student)
head.Name = "teng"
head.Age = 33
head.score = 100
Trans(InsertHead(head))
}
编译执行
如果不想使用返回值的话,可以使用如下的代码
package main
import "fmt"
type Student struct {
Name string
Age int
score float32
}
func Trans(p *Student) {
for p != nil {
fmt.Println(*p)
p = p.Next
}
}
func InsertHead(r **Student) {
for i := 0;i < 10;i++ {
var stu *Student = &Student{
Name: fmt.Sprintf("stu%d",i),
Age: rand.Intn(100),
score: rand.Float32() * 100,
}
stu.Next = *r
*r = stu
}
}
func main() {
var head *Student = new(Student)
head.Name = "wang"
head.Age = 33
head.score = 99
InsertHead(&head)
Trans(head)
}
如果要删除其中某个节点,比如stu6
package main
import "fmt"
type Student struct {
Name string
Age int
score float32
Next *Student
}
func Trans(p *Student) {
for p != nil {
fmt.Println(*p)
p = p.Next
}
}
func InsertHead(r **Student) {
for i := 0;i < 10;i++ {
var stu *Student = &Student{
Name: fmt.Sprintf("stu%d",i),
Age: rand.Intn(100),
score: rand.Float32() * 100,
}
stu.Next = *r
*r = stu
}
}
func Delnode(m *Student) {
var prev *Student = m
for m != nil {
if m.Name == "stu6" { // 过滤条件
prev.Next = m.Next // 删除动作
break
}
prev = m
m = m.Next
}
}
func main() {
var head *Student = new(Student)
head.Name = "wang"
head.Age = 33
head.score = 99
InsertHead(&head)
Trans(head)
Delnode(head)
Trans(head)
}
如果想插入节点,代码可以如下
package main
import "fmt"
type Student struct {
Name string
Age int
score float32
Next *Student
}
func Trans(p *Student) {
for p != nil {
fmt.Println(*p)
p = p.Next
}
}
func InsertHead(r **Student) {
for i := 0;i < 10;i++ {
var stu *Student = &Student{
Name: fmt.Sprintf("stu%d",i),
Age: rand.Intn(100),
score: rand.Float32() * 100,
}
stu.Next = *r
*r = stu
}
}
func Delnode(m *Student) {
var prev *Student = m
for m != nil {
if m.Name == "stu6" { // 过滤条件
prev.Next = m.Next // 删除动作
break
}
prev = m
m = m.Next
}
}
func Addnode(a *Student,b *Student) {
for a != nil {
if a.Name == "stu5" {
b.Next = a.Next
a.Next = b
break
}
a = a.Next
}
}
func main() {
var head *Student = new(Student)
head.Name = "wang"
head.Age = 33
head.score = 99
InsertHead(&head)
Trans(head)
Delnode(head)
Trans(head)
var addnode *Student = new(Student)
addnode.Name = "mn"
addnode.Age = 27
addnode.score = 98
Addnode(head,addnode)
Trans(head)
}
2、二叉树
代码如下
package main
import "fmt"
type Student struct {
Name string
Age int
score float32
left *Student
right *Student
}
func Trans(p *Student) {
if p == nil {
return
}
fmt.Println(*p)
Trans(p.right)
Trans(p.left)
}
func main() {
var root *Student = new(Student)
root.Name = "stu01"
root.Age = 32
root.score = 90
var left1 *Student = new(Student)
left1.Name = "stu02"
left1.Age = 33
left1.score = 91
root.left = left1
var right1 *Student = new(Student)
right1.Name = "stu04"
right1.Age = 49
right1.score = 78
root.right = right1
var left2 *Student = new(Student)
left2.Name = "stu03"
left2.Age = 50
left2.score = 59
left1.left = left2
Trans(root)
}
3、结构体的强制转换
看下面的例子
package main
type Student struct {
Name int
}
var Stu Student //alias
func main() {
var a Student
a = Student{20}
fmt.Println(a)
var b Stu
a = b
fmt.Println(a)
}
保存报错
cannot use a (type Student) as type Stu in assignment
虽然Stu和Student字段一样,但他们是两个不同的类型,不能强制赋值,如果要使用可以先进行转换 a = Student(b)
4、结构体中的tag
先来看一个例子
package main
import {
"fmt"
"encoding/json"
}
type Student struct {
name string
age int
score int
}
func main() {
var a Student = Student{
name:"stu01",
age:18,
score:80,
}
data,err := json.Mashal(a)
if err != nil {
fmt.Println("json encode failed:",err)
return
}
fmt.Println(string(data))
}
编译执行
{}
再来一段代码
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string
Age int
Score int
}
func main() {
var a Student = Student{
Name: "stu01",
Age: 18,
Score: 80,
}
data, err := json.Marshal(a)
if err != nil {
fmt.Println("json encode failed:", err)
return
}
fmt.Println(string(data))
}
编译执行
{"Name":"stu01","Age":18,"Score":80}
再来一段代码
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
Score int `json:"score"`
}
func main() {
var a Student = Student{
Name: "stu01",
Age: 18,
Score: 80,
}
data, err := json.Marshal(a)
if err != nil {
fmt.Println("json encode failed:", err)
return
}
fmt.Println(string(data))
}
编译执行
{"name":"stu01","age":18,"score":80}
5、结构体中的匿名字段
如何访问结构体中的匿名字段
package main
import "fmt"
type Cart struct {
name string
age int
}
type Train struct {
Cart
int
start time.Time
}
func main() {
var t Train
t.name = "train"
t.age = 100
t.int = 300
fmt.Println(t)
}
编译执行
{{train 100} 300 {0 0 <nil>}}
等同于
package main
import "fmt"
type Cart struct {
name string
age int
}
type Train struct {
Cart
int
start time.Time
}
func main() {
var t Train
t.Cart.name = "train"
t.Cart.age = 100
t.int = 300
fmt.Println(t)
}
如果上面的两个结构体中含有相同的字段age,会输出什么呢
package main
import (
"fmt"
"time"
)
type Cart struct {
name string
age int
}
type Train struct {
Cart
int
start time.Time
age int
}
func main() {
var t Train
t.name = "train"
t.age = 100
t.int = 300
fmt.Println(t)
}
编译执行
{{train 0} 300 {0 0 <nil>} 100}
6、struct里方法的定义
package main
import "fmt"
type Student struct {
Name string
Age int
Score int
}
func (p Student) init(name string,age int,score int) {
p.Name = name
p.Age = age
p.Score =score
fmt.Println(p)
}
func main() {
var stu Student
stu.init("stu",10,20)
}
编译执行
{stu 10 20}
init方法就是作用与结构体变量p,stu这里就等于p
上面的代码是通过方法对结构体变量进行初始化,下面可以通过使用初始化的结构体变量进行赋值验证初始化是否生效
package main
import "fmt"
type Student struct {
Name string
Age int
Score int
}
func (p Student) init(name string,age int,score int) {
p.Name = name
p.Age = age
p.Score =score
fmt.Println(p)
}
func (p Studnet) get() Student {
return p
}
func main() {
var stu Student
stu.init("stu",10,20)
stu1 := stu.get()
fmt.Println(stu1)
}
编译执行
{stu 10 20}
{ 0 0}
这与预期的结果不一样,说明stu的初始化没有成功,修改一下
package main
import "fmt"
type Student struct {
Name string
Age int
Score int
}
func (p *Student) init(name string,age int,score int) {
p.Name = name
p.Age = age
p.Score =score
fmt.Println(p)
}
func (p Studnet) get() Student {
return p
}
func main() {
var stu Student
(&stu).init("stu",10,20)
stu1 := stu.get()
fmt.Println(stu1)
}
编译执行
&{stu 10 20}
{stu 10 20}
这里之所以要传入一个地址过去,是因为前面那个不合预期的代码中init方法里的p只是stu的一个副本,传入的值并没有生效,所以这里传入一个地址进去,但是在go里面针对结构体变量做了简化,可以将&stu写成stu,但是方法中init作用的而结构体变量p的类型需要写成指针形式*Student,也就是说在调用针对结构体的方法时根据结构体变量p的类型来自动判断stu传入地址类型还是值类型
其实任何一个自定义的类型都可以给它定义一个方法,来看下面一个例子
package main
import "fmt"
type integer int
func (p integer) print() {
fmt.Println(p)
}
func main() {
var a integer
a = 100
a.print()
}
编译执行输出
100
前面在给结构体变量定义方法时碰到变量类型是值类型还是指针类型的问题,这里同样会涉及到这个问题
package main
import "fmt"
type integer int
func (p integer) print() {
fmt.Println(p)
}
func (p *integer) set(b integer) {
*p = b
}
func main() {
var a integer
a = 100
a.print()
a.set(1000)
a.print()
}
编译执行
100
1000
7、继承
go里的继承是通过匿名字段实现的
package main
import "fmt"
type Car struct {
weight int
name string
}
func (p *Car) Run() {
fmt.Println("running")
}
type Bike struct {
Car
lunzi int
}
type Train struct {
c Car
}
func main() {
var a Bike
a.weight = 100
a.name = "bike"
a.lunzi = 200
a.Run()
fmt.Println(a)
var b Train
b.c.weight = 300
b.c.name = "train"
b.Run()
}
编译执行
running
{{100 bike} 200}
running