结构体与指针
原创大约 5 分钟
结构体
package main
import (
"fmt"
)
// 用二维数组定义人的集合
var Persons [][]interface{}
// 可以用结构体来实现这种功能
// 定义结构体
type Person struct {
Name string
Age int
Address string
Height float64
}
type User struct {
Name string
Age int
}
/**
* 结构体嵌套
*/
// 第一种结构体嵌套的方式
type Student1 struct {
user User
score float64
}
// 第二种结构体嵌套的方式:匿名嵌套
type Student2 struct {
User
score float64
}
// 第三种结构体嵌套的方式:匿名嵌套 + 覆盖
type Student3 struct {
User
score float64
Name string
}
// 第四种结构体嵌套
type Student4 struct {
Name string
Age int
Address struct {
Province string
City string
Street string
}
}
/**
* 为结构体绑定方法:
*
* 格式1(值传递):func (s structType) funcName(param1 param1Type, ...) (returnVal1 Type1, ...) { ... }
* ↑ ↑ ↑ ↑ ↑
* 接收器(值传递) 方法名 方法参数 方法返回值 方法体
*
* 格式2(引用传递):func (s *structType) funcName(param1 param1Type, ...) (returnVal1 Type1, ...) { ... }
* ↑ ↑ ↑ ↑ ↑
* 接收器(引用传递) 方法名 方法参数 方法返回值 方法体
*/
// 注意:前面的 (p Person) 并不是方法的参数,而是绑定的结构体的类型和变量,也叫“接收器”
// go对结构体的指针做了优化:无论接收器是值类型还是指针类型,都可以接收并正确使用值传递类型
func (p *Person) GetName() string {
// 如果希望改变p的值,那么只能通过引用传递才能实现
p.Age = 22
return p.Name
}
func main() {
// 如果不使用结构体,那么保存Person的信息会比较麻烦
Persons = append(Persons, []interface{}{"lixingyun", 19, "china", 1.81})
// 创建并向结构体中保存数据
// 在同一行中赋值
person1 := Person{"lixingyun", 19, "china", 1.81}
// 在不同的行中赋值
person2 := Person{
Name: "lixingyun",
Age: 19,
Address: "china",
Height: 1.81,
}
// 也可以只初始化需要的字段
person3 := Person{
Name: "lixingyun",
Address: "china",
}
// 保存person数据
var persons1 []Person
// 第一种保存方式
persons1 = append(persons1, person1, person2, person3)
// 第二种保存方式
persons1 = append(persons1, Person{
Name: "lixingyun",
Height: 1.81,
})
// 第三种保存方式:一次初始化多个Person
persons2 := []Person{
{
Name: "lixingyun",
Address: "china",
},
{
Name: "lixingyun",
Height: 1.81,
},
{
Age: 19,
Address: "china",
},
}
fmt.Println(persons2)
// 第四种保存方式:一次初始化一个Person
var person4 Person
person4.Name = "lixingyun"
person4.Age = 19
person4.Height = 1.81
fmt.Println(person4)
// 第五种保存方式:一次初始化一个Person
person5 := Person{}
person5.Name = "wanglin"
fmt.Println(person5)
// 匿名结构体
address := struct {
Province string
City string
Street string
}{
Province: "beijing",
City: "beijing",
Street: "changan",
}
fmt.Println(address)
// 使用第一种结构体嵌套
stu1 := Student1{
user: User{
Name: "lixingyun",
},
score: 100,
}
stu1.user.Age = 20
fmt.Println(stu1)
// 使用第二种结构体嵌套
stu2 := Student2{}
stu2.Name = "lixingyun"
stu2.Age = 19
stu2.score = 100
fmt.Println(stu2)
// 使用第三种结构体嵌套
stu3 := Student3{
User{
Name: "lixingyun",
Age: 19,
},
100,
"wanglin",
}
// 设置的是外层的字段,外层字段的优先级比匿名结构体高
stu3.Name = "xiaoyan"
fmt.Println(stu3)
// 调用结构体定义的方法,就等同于调用“类”的方法
person6 := Person{
Name: "lixingyun",
}
// 这种调用方式等同于 print(p)
fmt.Println(person6.GetName())
fmt.Println(person6)
}
指针的一般性质
package main
import "fmt"
type User struct {
name string
age int
}
// 传递指针类型
func changeName(user *User) {
user.name = "lixingyun"
}
// 交换两个变量的值
func swap(a, b *int) {
*a, *b = *b, *a
}
func main() {
// ptr是一个指针,指向变量 a 的内存地址
var a int = 10
var ptr *int = &a
fmt.Println(ptr)
// 此时 “*” 不是用于指针定义,而是表示访问指针指向的变量,也就是取指针的值
fmt.Println(*ptr)
// 修改指针指向的变量的值
*ptr = 20
fmt.Println(a)
var b int = 30
// 将变量b的地址给指针,&表示取变量的地址
ptr = &b
// 取出指针的值
fmt.Println(*ptr)
fmt.Println(a)
// 结构体
user := User{
name: "bear",
age: 18,
}
fmt.Println(user)
// 取址符号
changeName(&user)
fmt.Println(user)
// 下面两种运用指针的方式,在go中都是合法的,也就是说:
// 可以像普通变量一样访问指针对象中的值,也可以把它当作指针来取地址
// go语言同时也限制了指针的运算,也就是指针是只读的
// 但同时go也提供了一个unsafe包,可以绕过go语言的指针限制
pi := &user
(*pi).name = "wanglin"
fmt.Println(user)
pi.name = "xiaoyan"
fmt.Println(user)
fmt.Println("==========================")
// 指针的初始化
var p *int
// 未初始化的指针都是nil
fmt.Println(p)
p = new(int)
fmt.Println(p)
*p = 10
fmt.Println(*p)
fmt.Println(p)
var i int = 22
p = &i
fmt.Println(p)
fmt.Println(*p)
var userPtr *User
// 这里指针未做初始化,打印结果为nil
fmt.Println(userPtr)
userPtr = &user
fmt.Println(userPtr.name)
fmt.Println((*userPtr).age)
fmt.Println("-----------------")
// 第一种初始化方式
userPtr = &User{}
fmt.Println("第一种初始化方式 ==> ", userPtr.name)
fmt.Println("-----------------")
// 第二种初始化方式
var user3 User
userPtr = &user3
fmt.Println("第二种初始化方式 ==> ", userPtr.name)
fmt.Println("-----------------")
// 第三种初始化方式
userPtr = new(User)
fmt.Println("第三种初始化方式 ==> ", userPtr.name)
// 对于容器,例如slice、map、channel,初始化推荐使用make()方法
// 对于指针,初始化推荐使用new()方法
// 另外,map必须初始化
fmt.Println("==========================")
// 使用swap交换指针的值
var c, d int = 10, 20
swap(&c, &d)
fmt.Println(c, d)
// 这种python方式可以交换变量值
x, y := 1, 2
x, y = y, x
fmt.Println(x, y)
}
nil的性质
package main
import "fmt"
type User struct {
name string
age int
test func()
}
func main() {
// 不同类型的数据,默认值是不一样的
/*
bool false
number 0
string ""
array []
struct {}
pointer nil
slice nil
map nil
channel nil
function nil
interface{} nil
*/
// 因此能够和nil进行判等的类型也只有:pointer、slice、map、channel、function、interface{},
// 而其他类型是不能和nil判等的,例如 i int 就不能和nil判等
var ps1 []User // nil silce
if ps1 == nil {
fmt.Println("yes")
}
var ps2 = make([]User, 0) // empty slice
// 这里ps2 不等于 nil
if ps2 == nil {
fmt.Println("yes")
}
// 对m1和nil作判等,结果为true
// nil map
var m1 map[string]string
// m1等于nil
if m1 == nil {
fmt.Println("yes")
}
// 但是下面的操作却不会抛异常
for key, value := range m1 {
fmt.Println(key, value)
}
// 即使取m1的值,也不会抛异常
fmt.Println("m1['key'] ==> ", m1["key"])
// 当赋值时,却抛异常了
// m1["key"] = "value" // 这行代码会抛异常:panic: assignment to entry in nil map
// 所以在声明具有nil值的数据类型时,最好使用make或new方式进行初始化,保证程序运行的安全
// empty map
var m2 = make(map[string]string, 0)
// m2不等于nil
if m2 == nil {
fmt.Println("yes")
}
for key, value := range m2 {
fmt.Println(key, value)
}
fmt.Println("m2['key'] ==> ", m2["key"])
m2["key"] = "value"
fmt.Println("m2['key'] ==> ", m2["key"])
}
感谢支持
更多内容,请移步《超级个体》。