10.Go语言·结构体字段

main.go

// Go语言·面向对象编程·结构体字段
package main

//  包的本质是创建不同的文件夹
//  go的每一个文件都是属于一个包的
//  go以包的形式管理项目和文件目录。
//  import "包的路径"
import (
     utils "day19/model/utils" 
    _"fmt"
)

var content string = `
————————————————Go语言·面向对象编程·结构体字段————————————————————
一、结构体字段

`


func main() {
    utils.Entry()
}

utils.go

// Go语言·面向对象编程·结构体字段
package utils

import (
    "fmt"
    "encoding/json"
)

var layout string = "2006-01-02"

// 全局变量
var Author string = "Jhou Shuai"

// 全局变量
var Description string = "Go语言·面向对象编程·结构体字段"

//全局变量:使用反引号来定义多行字符串
var Content string = `
————————————————Go语言·面向对象编程·结构体字段————————————————————
一、结构体 struct
    值类型
    自定义的数据类型
    type 结构体名称 struct {
        Field1 type
        Field2 type 
    }
    结构体字段=属性=field
    字段类型:基本数据类型,数组,或引用类型(切片,map)
    结构体(类)
    结构体变量(实例)
    1.将一类事物的特征提取出来,形成一个新的数据类型,就是一个结构体。
    2.通过这个结构体,我们可以创建多个变量(实例、对象)
    3.声明、创建一个结构体时,已分配空间。如果没有赋值,则默认为空值(或零值)
        结构体字段数据类型       默认值
        bool类型                false
        数值                      0
        字符串                    ""
        ****指针,slice,map的零值都是nil ,即还没有分配空间,
        ****温馨提示:使用的时候,需要先make
    4.不同的结构体变量的字段是独立,互不影响,一个结构体变量字段的改变,不影响另
    外一个,结构体是值类型。
二、注意事项和使用细节
    1.结构体的所有字段在内存中是连续的
        int 8个字节,string 16个字节
        // cat的地址: 0xc000020140 = &cat.Name
        // cat.Name 的地址: 0xc000020140
        // cat.Age  的地址: 0xc000020150  0xc000020150 = 0xc000020140 + 16
        // cat.Color的地址: 0xc000020158  0xc000020158 = 0xc000020150 + 8
        // cat.Hobby的地址: 0xc000020168  0xc000020168 = 0xc000020158 + 16
    2.结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字、个数、类
    型)
        type A struct {
            Num int
        }

        type B struct {
            Num int
        }
        var a A
        var b B
        a = A(b)
    3.结构体进行type重新定义(相当于取别名),Go语言认为是新的数据类型,但是相互间可以强
    制转换转
        type Student struct {
            Name string
            Age int
        }

        type Stu Student

        var stu1 Student
        var stu2 Stu
        // 强制转换
        stu2 = Stu(stu1)

        type integer int
        var i integer = 10
        var j int = 20
        // 强制转换
        j = int(i)
    4.struct的每个字段上,可以写一个tag标签,tag可以通过反射机制获取
    序列化[json格式],反序列化
        type Monster struct {
            Name string    tag标签
            Age int        tag标签 
            Skill string   tag标签
        }
        // 详见如下函数:
        jsonStruct()

`


type Cat struct {
    Name string
    Age int
    Color string
    Hobby string
}


type Person struct {
    Name string
    Age int
    Scores [5]float64 //数组
    ptr *int      //指针 new 
    slice []int   //切片 make
    myp map[string]string  //map make
}


type Monster struct {
    Name string   `json:"name"`     //tag标签
    Age int       `json:"age"`      //tag标签
    Skill string  `json:"skill"`    //tag标签
}


type Point struct {
    x int
    y int
}

type Rect struct {
    left,right Point
}

type Rect2 struct {
    left,right *Point
}


/**
 * [Init 入口]
 * @author Jhou Shuai
 * @datetime 2019-05-18T11:58:33+0800
 */
func Entry() {
    jsonStruct()
}


func structInit() {
    // 1.结构体Cat和结构体变量cat
    // 2.类Cat和实例(对象)cat
    // 3.结构体是值类型
    var cat Cat
    cat.Name = "小喵喵"
    cat.Age  = 3
    cat.Color="白色"
    cat.Hobby="吃<。)#)))≦"
    fmt.Println(cat)

    fmt.Println()
    fmt.Printf("cat的地址: %p \n\n",&cat)
    
    fmt.Printf("cat.Name 的地址: %p \n",&cat.Name)
    fmt.Printf("cat.Age  的地址: %p \n",&cat.Age)
    fmt.Printf("cat.Color的地址: %p \n",&cat.Color)
    fmt.Printf("cat.Hobby的地址: %p \n",&cat.Hobby)
    
    // cat的地址: 0xc0000660c0  == &cat.Name 的地址
    // cat.Name 的地址: 0xc0000660c0
    // cat.Age  的地址: 0xc0000660d0   0xc0000660d0 = 0xc0000660c0 + 16
    // cat.Color的地址: 0xc0000660d8   0xc0000660d8 = 0xc0000660d0 + 8
    // cat.Hobby的地址: 0xc0000660e8   0xc0000660e8 = 0xc0000660d8 + 16
}


func structUsedDetail() {
    // 值类型
    // ****字段数据类型:指针,slice,map的零值都是nil ,即还没有分配空间,
    // ****温馨提示:使用的时候,需要先make
    
    // 定义结构体变量
    // { 0 [0 0 0 0 0]  [] map[]}
    var p1 Person

    if p1.ptr == nil {
        fmt.Println("p1.ptr[指针] is nil , 没分配空间")
    }

    if p1.slice == nil {
        fmt.Println("p1.slice[切片] is nil , 没分配空间")
    }

    if p1.myp == nil {
        fmt.Println("p1.myp[map] is nil , 没分配空间")
    }

    // 出错啦:
    // panic: runtime error: invalid memory address or nil pointer dereference
    // *p1.ptr = 100
    // 正确使用
    p1.ptr = new(int)
    fmt.Println(*p1.ptr) // 0 
    *p1.ptr = 100

    // 出错啦:
    // panic: runtime error: index out of range
    // p1.slice[0] = 100

    // 正确使用
    // 使用切片,一定要先make
    p1.slice = make([]int,2)
    p1.slice[0] = 100
    p1.slice[1] = 110

    
    // 出错啦:
    // panic: assignment to entry in nil map
    // p1.myp["No001"] = "妯娌"

    // 使用map,一定要先make
    p1.myp = make(map[string]string)
    p1.myp["No001"] = "妯娌"
    p1.myp["No002"] = "咕力咕力"

    fmt.Println(p1)
    fmt.Println()

    // 不同的结构体变量的字段是独立,互不影响,一个结构体变量字段的改变,不影响另
    // 外一个,结构体是值类型。
    
    var m1 Monster
    m1.Name = "青龙"
    m1.Age  = 2000

    m2 := m1

    m2.Name = "朱雀"
    fmt.Println(m1)
    fmt.Println(m2)
}



func createStructMethod() {
    // 方式一:
    var p1 Person
    fmt.Println(p1)  //{ 0 [0 0 0 0 0]  [] map[]}

    // 方式二:
    // {周帅 18 [34 45.23 12 44 55] 0xc00006a080 [0 0] map[]}
    // var p2 = Person{"周帅",18,[5]float64{34,45.23,12,44,55},new(int),make([]int,2),make(map[string]string)}
    // var m Monster = Monster{}
    m1 := Monster{"白虎",1000,"圣光降临"}  
    fmt.Println(m1) //{白虎 1000}

    // 方式三:指针
    var m2 *Monster = new(Monster)
    // m2是指针,给字段赋值的标准方式:
    (*m2).Name = "狐狸"
    (*m2).Age  = 998   
    fmt.Println(*m2)  //{狐狸 998}

    // 可以改写成如下(简化,但底层会自动加上取值运算*):
    m2.Name = "饕餮"
    m2.Age  = 100000

    fmt.Println(*m2) //{饕餮 100000}

    // 方式四:指针 地址符&
    // var m3 *Monster = &Monster{"小乌龟",10000}
    var m3 *Monster = &Monster{}
    // m3是指针,给字段赋值的标准方式:
    (*m3).Name = "小兔子"
    (*m3).Age  = 3   
    fmt.Println(*m3)  //{小兔子 3}



    // 可以改写成如下(简化,但底层会自动加上取值运算*):
    m3.Name = "白天鹅"
    m3.Age  = 2
    fmt.Println(*m3) //{白天鹅 2}
}


/**
 * [memoryStruct 结构体内存分配]
 * @author Jhou Shuai
 * @datetime 2019-06-02T10:24:47+0800
 */
func memoryStruct(){
    // 1.结构体Cat和结构体变量cat
    // 2.类Cat和实例(对象)cat
    // 3.结构体是值类型
    var cat Cat
    cat.Name = "小喵喵"
    cat.Age  = 3
    cat.Color="白色"
    cat.Hobby="吃<。)#)))≦"
    fmt.Println(cat)

    fmt.Println()
    fmt.Printf("cat的地址: %p \n\n",&cat)
    fmt.Printf("cat.Name 的地址: %p \n",&cat.Name)
    fmt.Printf("cat.Age  的地址: %p \n",&cat.Age)
    fmt.Printf("cat.Color的地址: %p \n",&cat.Color)
    fmt.Printf("cat.Hobby的地址: %p \n",&cat.Hobby)
    // cat的地址: 0xc000020140 = &cat.Name
    // cat.Name 的地址: 0xc000020140
    // cat.Age  的地址: 0xc000020150  0xc000020150 = 0xc000020140 + 16
    // cat.Color的地址: 0xc000020158  0xc000020158 = 0xc000020150 + 8
    // cat.Hobby的地址: 0xc000020168  0xc000020168 = 0xc000020158 + 16

    fmt.Println()
    cat1 := cat
    cat1.Name = "小可爱"
    fmt.Printf("cat1的地址: %p \n\n",&cat1)
    fmt.Printf("cat1.Name 的地址: %p \n",&cat1.Name)
    fmt.Printf("cat1.Age  的地址: %p \n",&cat1.Age)
    fmt.Printf("cat1.Color的地址: %p \n",&cat1.Color)
    fmt.Printf("cat1.Hobby的地址: %p \n",&cat1.Hobby)
    // cat1的地址: 0xc000020200 = &cat1.Name
    // cat1.Name 的地址: 0xc000020200
    // cat1.Age  的地址: 0xc000020210  0xc000020210 = 0xc000020200 + 16
    // cat1.Color的地址: 0xc000020218  0xc000020218 = 0xc000020210 + 8
    // cat1.Hobby的地址: 0xc000020228  0xc000020228 = 0xc000020218 + 16
    // 
    // 
    // cat1 与 cat 值拷贝, 是两个独立的空间,其他一个修改,不影响对方

    fmt.Println()
    fmt.Printf("[修改之前]cat的地址: %p ,cat的值: %v \n\n",&cat,cat)
    var cat2 *Cat = &cat
    // cat2 := &cat
    cat2.Name = "小美丽"
    (*cat2).Color="黑色"
    // *cat2.Color="黑色"
    // 报错:因为点.运算符优先级比*高。
    // *(cat2.Color) 
    cat.Hobby="贪玩(๑ŐдŐ)b"
    fmt.Printf("cat2本身的地址: %p ,cat2的值(cat的地址): %p \n\n",&cat2,cat2)
    fmt.Printf("cat2.Name 的地址: %p \n",&cat2.Name)
    fmt.Printf("cat2.Age  的地址: %p \n",&cat2.Age)
    fmt.Printf("cat2.Color的地址: %p \n",&cat2.Color)
    fmt.Printf("cat2.Hobby的地址: %p \n",&cat2.Hobby)

    fmt.Println()
    fmt.Printf("[修改之后]cat的地址: %p ,cat的值: %v \n\n",&cat,cat)
    fmt.Printf("cat.Name 的地址: %p \n",&cat.Name)
    fmt.Printf("cat.Age  的地址: %p \n",&cat.Age)
    fmt.Printf("cat.Color的地址: %p \n",&cat.Color)
    fmt.Printf("cat.Hobby的地址: %p \n",&cat.Hobby)

    // [修改之前]cat的地址: 0xc0000660c0 ,cat的值: {小喵喵 3 白色 吃<。)#)))≦}

    // cat2本身的地址:  0xc000096020 ,cat2的值(cat的地址): 0xc0000660c0
    // cat2.Name 的地址: 0xc0000660c0
    // cat2.Age  的地址: 0xc0000660d0
    // cat2.Color的地址: 0xc0000660d8
    // cat2.Hobby的地址: 0xc0000660e8

    // [修改之后]cat的地址: 0xc0000660c0 ,cat的值: {小美丽 3 黑色 贪玩(??д?)b}
    // cat.Name 的地址: 0xc0000660c0
    // cat.Age  的地址: 0xc0000660d0
    // cat.Color的地址: 0xc0000660d8
    // cat.Hobby的地址: 0xc0000660e8
}


func memoryStructInfo() {
    
    r1 := Rect{Point{1,2},Point{3,4}}


    fmt.Println()
    fmt.Printf("r1的地址: %p \n\n",&r1)
    fmt.Printf("r1.left.x  的地址: %p \n",&r1.left.x)
    fmt.Printf("r1.left.y  的地址: %p \n",&r1.left.y)
    fmt.Printf("r1.right.x 的地址: %p \n",&r1.right.x)
    fmt.Printf("r1.right.y 的地址: %p \n",&r1.right.y)

    // r1的地址:          0xc000012480
    // r1.left.x  的地址: 0xc000012480
    // r1.left.y  的地址: 0xc000012488
    // r1.right.x 的地址: 0xc000012490
    // r1.right.y 的地址: 0xc000012498
    // 
    // 两个*Point类型的本身地址是连续的
    // 指向的地址不一定连续的,编译器,系统分配
    r2 := Rect2{&Point{10,20},&Point{30,40}}
    fmt.Println()
    fmt.Printf("r2的地址: %p \n\n",&r2)
    fmt.Printf("r2.left 的本身地址: %p \n",&r2.left)
    fmt.Printf("r2.right 的本身地址: %p \n",&r2.right)

    // r2的地址: 0xc0000561c0
    // r2.left 的本身地址: 0xc0000561c0
    // r2.right 的本身地址: 0xc0000561c8


    fmt.Printf("r2.left  的指向地址: %p \n",r2.left)
    fmt.Printf("r2.right 的指向地址: %p \n",r2.right)
    // r2.left  的指向地址: 0xc00000a100
    // r2.right 的指向地址: 0xc00000a110
}


func jsonStruct() {
    m1 := Monster{"白虎",1000,"沐浴圣光"} 
    jsonStr,err := json.Marshal(m1)
    if err != nil {
        fmt.Println("json处理错误 ",err)
    }
    // {"Name":"白虎","Age":1000,"Skill":"沐浴圣光"}
    // {"name":"白虎","age":1000,"skill":"沐浴圣光"}
    fmt.Println(string(jsonStr))
}

你可能感兴趣的:(10.Go语言·结构体字段)