GO之结构体(1)

@爱耍流氓的唐僧  February 4, 2021

1.类型别名和自定义类型
//将MyInt定义为int类型

type MyInt int

通过type关键字的定义,MyInt就是一种新的类型,它具有int的特性。

类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。

type TypeAlias = Type

我们之前见过的rune和byte就是类型别名,他们的定义如下:

type byte = uint8
type rune = int32

微信图片_20210203165314.png

类型定义和类型别名的区别
//定义MyInt类型的变量方法
1.var m MyInt
m=10
2.var m MyInt = 10
3.m := MyInt(10)

//类型定义
type NewInt int
//类型别名
type MyInt = int

func main() {
    var a NewInt
    var b MyInt      
    fmt.Printf("type of a:%T\n", a) //type of a:main.NewInt
    fmt.Printf("type of b:%T\n", b) //type of b:int
}

结果显示a的类型是main.NewInt,表示main包下定义的NewInt类型。b的类型是int。MyInt类型只会在代码中存在,编译完成时并不会有MyInt类型。
类型别名只在代码编写过程有效,编译完成之后就不存在,内置的byte和rune都属于类型别名。

2.结构体
使用type和struct关键字来定义结构体,具体代码格式如下:

type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}
其中:
类型名:标识自定义结构体的名称,在同一个包内不能重复。
字段名:表示结构体字段名。结构体中的字段名必须唯一。
字段类型:表示结构体字段的具体类型。
type person struct {
name string
city string
age  int8
}

结构体实例化
只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。
结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。

var 结构体实例 结构体类型

示例:

package main

import "fmt"

type person struct {
    name string
    city string
    age  int8
}

func main() {
    var p1 person
    p1.name = "爱耍流氓的唐僧"
    p1.city = "苏州"
    p1.age = 23
    fmt.Printf("p1=%v\n", p1)  //p1={爱耍流氓的唐僧 苏州 23}
    fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"爱耍流氓的唐僧", city:"苏州", age:23}
}

匿名结构体
在定义一些临时数据结构等场景下还可以使用匿名结构体。

package main
     
import (
    "fmt"
)
     
func main() {
    var user struct{Name string; Age int}
    user.Name = "xiaohua"
    user.Age = 23
    fmt.Printf("%#v\n", user)
}

创建指针类型结构体

var p2 = new(person) //new 返回的是person类型的指针*person

//任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。

fmt.Printf("%T\n", p2)     //*main.person
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}

示例

package main

import "fmt"

type person struct{
    name,gender string
}

//go语言中函数传参数传的是拷贝,除非特指传地址
func f(x person){
    x.gender = "女"
}

func f2(x *person){
    //(*x).gender="女" //根据内存地址找到那个原变量,修改的就是原来的变量
    x.gender = "女" //语法糖,自动根据指针找到对应的变量
}

func main(){
    var p person
    p.name = "xiaowang"
    p.gender = "男"
    f(p)
    fmt.Println(p.gender)//男
    f2(&p)
    fmt.Println(p.gender)//男
    
    var p2 = new (person)
    p2.name = "lili" //语法糖,p2其实是一个指针,常规写法(*p2).name="lili"
    fmt.Println("%p\n",p2)//p2保存的值是一个内存地址,就是一个指针  
    fmt.Println("%p\n",&p2)//这个是p2这个变量的内存地址
    //结构体
    var p3 = person{  
        name:"huahua",
        gender:"男"
    }
    fmt.Println("%#v\n",p3)//p3类型是main.person结构体类型,person前面加个&,p3就变成了指针
    //使用值列表的形式初始化,值的顺序要和结构体定义是的字段顺序一致
    p4 := person{
        "hh",
        "女"
   }
   fmt.Println("%#v\n",p4) //p4类型是main.person结构体类型,person前面加个&,p4就变成了指针
}

结构体内存布局:
结构体占用一块连续的内存
结构体占用一块连续的内存。

type test struct {
    a int8
    b int8
    c int8
    d int8
}
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)

输出:

n.a 0xc0000a0070
n.b 0xc0000a0071
n.c 0xc0000a0072
n.d 0xc0000a0073

空结构体
空结构体是不占用空间的。

var v struct{}
fmt.Println(unsafe.Sizeof(v))  // 0

添加新评论