• Go语言基础


    Go的数据类型定义

    1. //运行第一个程序
    2. package main
    3. func main(){
    4. print("Hello World")
    5. }

    在GO语言中,一个程序只能有一个main包,对应只能有一个main方法,若无法满足这个条件,编译时将会报错。注释方式与PHP相同

    1. import的使用

    个人在使用import时,按照自己的编写习惯,依赖IDE的自动导包功能,实现包的导入。若导入包后没有使用,会编译出错。

    1. // 使用 . 来实现省略调用
    2. import . "fmt"
    3. // 在包前通过别名替代包名
    4. import stdio "fmt"
    5. // 包前使用 _ 作用为只调取包的init方法
    6. import _ "xxx"
    7. // 使用 () 实现多包导入
    8. import (
    9. "fmt"
    10. "os"
    11. )
    1. GO的可见性

    GO通过大小写来判别函数或者变量的权限,小写则为private,大写为public

    1. 定义数据结构

    GO定义数据类型比较有意思。在函数体内使用var定义的变量必须被使用,否则编译报错,申明外部变量没有限制

    1. 布尔类型

    布尔类型只有true与false两个值,不可用0与1替代

    1. 整形

    int类型根据使用的平台自动判别32位还是64位,可以手动定义指定的位数,例如 int8/uint16,定义了指定8位的int型/16位的无符号int型

    1. 浮点型

    浮点型有float32/float64,精确范围对应7/15位

    1. 复数类型

    complex64/complex128

    1. 其他值类型

    array struct string

    1. 引用类型

    slice map chan

    1. 接口类型

    inteface

    1. 函数类型

    func

    1. // 定义单个变量
    2. var hello = "Hello World"
    3. //定义多个变量
    4. var(
    5. num = 321
    6. str = "haha"
    7. )
    8. //定义常量,定义多个常量与变量的定义一样
    9. const PI =3.14
    1. 类型零值

    零值不等于空值,变量被声明为某种类型后的默认值,通常情况下整型的默认值为0,bool为false,string为空字符串

    1. 类型别名的定义

    利用type进行类型别名的定义

    1. type(
    2. chinese string
    3. intMax int64
    4. )
    1. 变量声明与赋值
    1. 变量的声明格式:var <name> <type>
    2. 变量的赋值格式:<name> = <value>
    3. 声明的同时赋值:var <name> [type] = <value>
    4. 自身推断赋值:var <name> = <value>
    5. 简写赋值(不可全局变量赋值使用) <name> := <value>
    1. 类型转换

    进行类型转换时,需要进行显式转换,并且需要保证两个不同的类型转换可以互相兼容

    1. var a = 'a'
    2. b := int(a)
    1. 常量的定义问题

    常量值在编译时已经确定,所以右侧的数据需要使用常量或者内置函数,变量不能进行常量的赋值。定义常量组时,如果不进行数值的赋值,将会将其数值赋值为其上行的数值。常量初始化规则定义了每一行的常量定义个数一致时,默认上行数值下行初始化将进行,否则报错。

    常量的iota是常量计数器,只在常量的表达式中使用。const中每新增一行常量声明将使iota计数一次

    1. // 赋值b,c均为hello字符串,赋值g,h为1,2
    2. const(
    3. a = "hello"
    4. b
    5. c
    6. e,f = 1,2
    7. g,h
    8. )
    9. // 输出 c为2 f为5
    10. const(
    11. a=1
    12. b
    13. c = iota
    14. d
    15. e
    16. f = iota
    17. )

    运算符

    1、运算符的基本介绍

    运算符是一种特殊的符号,可以表示数据的运算,赋值,比较等

    1)算术运算符

    5c8fa6809b4a44609072b3be32a9dc85.png

    2)赋值运算符

    839fbac84b87466e99fe2a5b60af6c3c.png

    3)比较运算符

    fcda8b8ec0c44ea6a70342edeecfcd65.png

    4)逻辑运算符

    c09696f85d9148439ed539186595e4a7.png

    5)位运算符

    4e330edbb4004f299c9a6d759534f12c.png

    6)其他运算符

    ff9af3bb3c28490fb2608d0a325afc58.png

    比较两个数的大小。

    1. package lee
    2. import "fmt"
    3. func Test(){
    4. a:=101
    5. b:=66
    6. if(a>b){
    7. fmt.Printf("a大一些")
    8. }else
    9. fmt.Printf("b大一些")
    10. }

    注意:go语言不支持三元运算符

    2、运算符的优先级

    f3c3727a9f814e40b58a570af2b26bfb.jpeg

    控制语句

    1. if条件判断

    if条件判断:在布尔表达式里面可以定义变量,并行赋值.变量的作用域只能作用在代码块里面.

    1. // 格式:
    2. if <布尔表达式>{
    3. 语句块
    4. }else if <布尔表达式>{
    5. 语句块
    6. }else{
    7. 语句块
    8. }
    9. // 定义i=5 输出5,下面一句会编译出错
    10. if i:=5;i<10{
    11. print(i)
    12. }
    13. print(i)
    1. 循环控制

    go可以使用for完成所有的循环操作,变量的作用域只能作用在代码块里面

    1. //经典for循环结构
    2. for i:=1;i<100;i++{
    3. print(i)
    4. }
    5. //单条件(while式)for循环结构
    6. for true{
    7. print("进入死循环")
    8. }
    9. // 无条件式
    10. for {
    11. print("hello")
    12. }
    1. switch语句

    switch不需要使用break,自动进行跳出,若需要连续条件选择,使用fallthrough。

    1. // 常规操作
    2. i := 3
    3. switch i{
    4. case 1:
    5. print(1)
    6. case 2:
    7. print(2)
    8. case 3:
    9. print(3)
    10. default:
    11. print(4)
    12. }
    13. // 在表达式中定义变量
    14. switch i=3; {
    15. case i==1:
    16. print(1)
    17. case i==2:
    18. print(2)
    19. case i==3:
    20. print(3)
    21. default:
    22. print(4)
    23. }
    24. // 使用fallthrough 输出2 3
    25. i := 2
    26. switch i{
    27. case 1:
    28. print(1)
    29. case 2:
    30. print(2)
    31. fallthrough
    32. case 3:
    33. print(3)
    34. default:
    35. print(4)
    36. }

    1. 跳转语句

    goto break continue 三者均可以执行跳转。
    goto用于调整执行位置。使用时,注意标签的位置
    break用于循环语句中跳出循环,并开始执行循环之后的语句
    continue不是跳出循环,而是跳过当前循环执行下一次循环语句,一般用于for 循环中,触发for增量语句的执行。

    1. //goto操作 这里输出 你好,这里是跳转
    2. START:
    3. print("你好,这里是跳转")
    4. os.Exit(1)
    5. for i:=1; i<3;i++ {
    6. goto START
    7. }
    8. print("这里是测试是否跳转")
    9. // break操作 输出 跳出了循环start
    10. START:
    11. for i:=1;i<=2 ;i++ {
    12. break START
    13. }
    14. print("跳出了循环start")
    15. //continue操作 输出 2 4 6 8
    16. for i:=1;i<10 ;i++ {
    17. if(i%2!=0){
    18. continue
    19. }
    20. print(i)
    21. }

    数组

    数组的定义格式 var < name > [n]< type >。
    数组赋值时,两个不同长度变量无法进行赋值操作。
    go语言中 数组是一个值类型,使用时,直接对值进行操作,而不是地址引用。
    两个长度相同的数组可以实现等于号判断。

    1. //常规定义
    2. var a [5]int
    3. //简写
    4. a:=[2]int{1,2}
    5. //索引赋值:将最后一个元素定义为5 其余为默认值
    6. a:=[20]int{19:5}
    7. //自动判断数组长度定义,使用三个点 编译器将会自动判断长度赋值
    8. a:=[...]int{11:5}
    9. //定义指针
    10. var p *[5]int
    11. //定义多维数组
    12. var arr = [2][3]int{}
    13. arr := [2][3]int{}
    14. //GO语言冒泡排序
    15. package main
    16. import "fmt"
    17. func main(){
    18. var a = [5]int{5,9,4,1,6}
    19. num := len(a)
    20. for i:=0;i
    21. for j:=i+1;j
    22. if(a[i]>a[j]){
    23. temp:=a[j]
    24. a[j] = a[i]
    25. a[i] = temp
    26. }
    27. }
    28. }
    29. fmt.Print(a)
    30. }

    切片 

    一、切片的定义

     在Go语言中,切片(Slice)是数组的一个引用。

    它会生成一个指向数组的指针,并通过切片长度关联到底层数组部分或者全部元素。

    切片还提供了一系列对数组的管理功能(append、copy),可以随时动态扩充存储空间,并且可以被随意传递而不会导致所管理的数组元素被重复复制。

    根据以上特征,切片通常被用来实现变长数组,而且操作灵活。

    切片的数据结构原型定义如下:

    src/ pkg/ runtime/ runtime. h

    1. struct Slice
    2. {                   //must not move anything
    3.     byte* array;    //actual data
    4.     unit32 len;     //number of elements
    5.     unit32 cap;     //allocated number of elements
    6. };

    由切片数据结构的原型定义可以看到,它抽象为以下三个部分:

    指向被引用的底层数组的指针。
    切片中元素的个数。
    切片分配的存储空间。

    二、切片的声明与创建

    切片声明与创建的方法有三种:

            1、基于底层数组创建

            2、直接创建

            3、使用make()函数创建。

    1、基于底层数组创建

    在创建切片时,可以基于一个底层数组,切片可以只使用数组的一部分元素或所有元素,甚至可以创建一个比底层数组还要大的数组切片,因为切片可以动态增长。创建切片的格式如下:

    var sliceName [ ]dataType

    说明:

    切片名的命名规则和变量名相同,遵循标识符命名规则。
    在创建切片时,不要指定切片的长度。
    切片的类型可以是Go语言的任何基本数据类型。

    例如:

    var slice1 [] int
          上例中定义了一个整型切片slicel,注意不要指定切片长度,如果指定了长度就成了定义数组了。

    当一个切片定义好以后,如果还没有被初始化,默认值为nil, 而且切片的长度为0。切片的长度可以使用内置函数len()获取,还可以使用内置函数cap()获取切片的内存容量。

    所以,当一个切片定义好以后,还要对切片进行初始化,这样切片才可用。对于上例,假如已经定义了一个整型数组array1,则切片slice1的初始化方式如下:

    slice1 = array1[start : end]

     从上式可以看到,Go语言支持以array1[start : end]的方式基于一个底层数组来生成切片,即切片引用的数组元素由array1[start]到 array1[end],但不包含array1[end]。

    如果要引用底层数组的所有元素,可采取的方式如下:

    1. slicel = array1
    2. slice1 = array1[ :]
    3. slicel = array1[0: len(array1)] 

    2、直接创建切片

    直接创建切片,即在定义切片的时候初始化切片元素,例如:

    var slice1 =[]int{1,2,3,4,5}

    3、使用make函数创建切片

    内置函数make()用于灵活的创建切片

    var slicel = make([]int,5)

    上式创建了一个有5个元素的整型切片slicel,元素的初值为0。
    在使用make()函数创建切片时,还可以为切片元素预留存储空间。例如:

    var slice1 = make([]int, 5,10)

    上式表示,创建整型切片slice1,元素个数为5,元素初值为0,并预留10个元素的存储空间。

    三、切片元素的访问与遍历

    切片元素的遍历和数组元素的遍历一样,要通过元素下标访问,另外也可以使用关键字range遍历所有切片元素。

    切片元素访问的一般格式如下:

    sliceName [sliceIndex]

    遍历同数组,使用range关键字表达式,有两个返回值,第一个是元素的下标,第二个是元素的值。

    四、切片的操作

    1、切片元素的增加

            可以使用append()函数向切片尾部添加新元素,这些元素保存到底层数组。append并不会影响原来切片的属性,它返回变更后新的切片对象。

            与数组相比,除了都有长度(length)以外,切片多了一个容量(capacity)的概念,即切片中元素的个数和分配的存储空间是两个不同的值。如果在增加新元素时超出cap的限制,则底层会重新分配一块“够大”的内存,一般来说是将原来的内存空间扩大二倍,然后再将数据从原来的内存复制到新的内存块。

    2、切片元素的复制

    使用切片长时间引用“超大”的底层数组,会导致严重的内存浪费。

    可以新建一个小的slice对象,然后将所需的数据复制过去。

    函数copy()可以在切片之间复制元素,能够复制的数量取决于复制方和被复制方的长度值,通常取最小值。

    需要注意的是,在同一底层数组的不同切片间复制元素时,元素位置会发生重叠。

    1. //切片的复制
    2. package main
    3.  
    4. import(
    5.     "fmt" 
    6. )
    7.  
    8. func main() {
    9.     var slicel= []int{1,2,3,4,5,6,7,8,9,10}
    10.     var slice2 = make([ ] int,3, 5)
    11.     var n int
    12.     //只能复制三个元素
    13.     n= copy(slice2,slice1)
    14.     fmt. Println(n, slice2, len( slice2), cap(slice2))
    15.     //slice3和slice1指向同一底层数组
    16.     slice3 : = slice1[3:6]
    17.     //复制后元素重叠
    18.     n= copy(slice3, slice1[1:5])
    19.     fmt.Println(n, slicel, slice3)
    20. }
    21. 编译并运行程序输出结果为:
    22. 3 [1 2 3] 3 5
    23. 3 [1 2 3 2 3 4 7 8 9 10] [2 3 4]

            通过输出结果可以看出,在将slice1复制到slice2时,由于slice2的长度最小为3,所以只能将slice1的前三个元素复制给slice2。而将slicel1复制到slice3 时,由于slicel1和slice3指向同一个底层数组,所以复制后元素重叠。slice3刚创建时,它引用的是底层数组的[4,5,6]三个元素,复制后slice1将[2,3,4]三个元素复制给slice3 ,所以最后slice3 的元素[2,3,4]覆盖了slice1的元素[4,5,6]。

    3、排序和搜索切片

            标准库中的sort包提供了对整型、浮点型和字符串类型切片进行排序的函数,检查一个切片是否排序好的函数,以及使用二分搜索算法在一个有序切片中搜索-一个元素的函数。 同时提供了通用sort.Sort ()和sort.Search ()函数,可用于任何自定义的数据。

    函数

    1.简介

    • 函数:为完成某一功能的程序指令(语句)的集合
    • 在Go中,函数分为自定义函数、系统函数

    2.基本语法

    1. func 函数名(形参列表) (返回值列表) {
    2. 函数体
    3. return 返回值列表
    4. }

    3.递归

    • 函数体内调用自己
    1. package main
    2. import "fmt"
    3. func test(n int) {
    4. if n > 2 {
    5. n--
    6. test(n)
    7. }
    8. fmt.Println("n = ", n)
    9. }
    10. func main() {
    11. test(4)
    12. }
    • 执行一个函数时,就创建一个新的受保护的独立空间
    • 函数的局部变量是独立的,不会相互影响
    • 递归必须向退出递归的条件逼近,否则就会出现无限递归
    • 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用就将结果返回给谁。

    4.递归练习

    • 1.求第N个斐波那契数列对应的值
    1. package main
    2. import "fmt"
    3. func feb(n int) int {
    4. if n <= 2 {
    5. return 1
    6. }
    7. return test(n-1) + test(n-2)
    8. }
    9. func main() {
    10. result := feb(6)
    11. fmt.Println(result)
    12. }
    • 2.已知函数f(1) = 3f(n) = 2*f(n-1) + 1,求f(n)的值
    1. package main
    2. import "fmt"
    3. func test(n int) int {
    4. if n == 1 {
    5. return 3
    6. }
    7. return 2*test(n-1) + 1
    8. }
    9. func main() {
    10. result := test(6)
    11. fmt.Println(result)
    12. }
    • 3.有一堆桃子,猴子第一天吃了其中的一半,并在多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天准备吃时,发现只剩了1个桃子问题:最初总共多少个桃子

    49b323c512af34155b47e6ec4a90edb2.png

    • 从上图可以看出其通项公式为:(1 + f(n+1))* 2n代表天数
    1. package main
    2. import "fmt"
    3. func getPeach(n int) int {
    4. if n == 10 {
    5. return 1
    6. }
    7. return (1 + getPeach(n+1)) * 2
    8. }
    9. func main() {
    10. result := getPeach(1)
    11. fmt.Println(result)
    12. }

    5.函数使用注意细节

    • 1.函数的形参列表和返回值列表都可以是多个

    • 2.形参列表和返回值列表都可以是值类型或引用类型

    • 3.函数命名遵循标识符命名规则,首字母不能是数字,首字母如果是大写,则可以被其他文件或包调用,否则只能在当前文件被调用

    • 4.函数的变量是局部的,函数外不生效

    • 5.基本类型和数组默认都是值传递,即进行值拷贝,在函数内修改,不会影响到原来的值

    • 6.如果希望函数内的变量能修改函数外的变量,可以传递变量的地址&,函数内以指针方式操做变量

    • 7.Go函数不支持重载(即一个包下不允许存在同名的函数)

    • 8.在Go语言中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量可以对函数进行调用

    • 9.函数既然是一种数据类型,因此在Go语言中,函数可以作为形参,并且调用

      1. package main
      2. import "fmt"
      3. func test(myFunc func(int, int) int, num1, num2 int) int {
      4. return myFunc(num1, num2)
      5. }
      6. func getSum(a, b int) int {
      7. return a + b
      8. }
      9. func main() {
      10. resp := test(getSum, 1, 2)
      11. fmt.Println(resp)
      12. }
    • 10.为了简化数据类型定义,Go语言支持自定义数据类型

      • 基本语法:type myInt int,myInt就可以当作int类型来使用,但不完全一致,不能将一个myInt类型的值直接赋值给int类型的变量,需要使用int()来强转一下。
      • 也可以使用type来定义一个函数类型
      1. package main
      2. import "fmt"
      3. type myFuncType func(int, int) int
      4. func test(myFunc myFuncType, num1, num2 int) int {
      5. return myFunc(num1, num2)
      6. }
      7. func getSum(a, b int) int {
      8. return a + b
      9. }
      10. func main() {
      11. resp := test(getSum, 1, 2)
      12. fmt.Println(resp)
      13. }
    • 11.支持对函数返回值命名

      1. package main
      2. import "fmt"
      3. type myFuncType func(int, int) int
      4. func test(myFunc myFuncType, num1, num2 int) int {
      5. return myFunc(num1, num2)
      6. }
      7. // 将函数返回值重命名为sum
      8. func getSum(a, b int) (sum int) {
      9. sum = a + b
      10. return
      11. }
      12. func main() {
      13. resp := test(getSum, 1, 2)
      14. fmt.Println(resp)
      15. }
    • 12.使用_来忽略返回值

    • 13.Go支持可变参数参数名...,必须放在参数最后位置

      1. package main
      2. import "fmt"
      3. func getSum(args ...int) int {
      4. sum := 0
      5. for i := 0; i < len(args); i++ {
      6. sum += args[i]
      7. }
      8. return sum
      9. }
      10. func main() {
      11. resp := getSum(1, 2, 3)
      12. fmt.Println(resp)
      13. }

    6.init函数

    • 每个源文件都可以包含一个init函数,该函数会在main函数前执行,被Go运行框架调用。
    • 通常在init函数中完成初始化工作,每个文件都可以有一个init函数
      1. package main
      2. import "fmt"
      3. func init() {
      4. fmt.Println("init函数自动执行")
      5. }
      6. func main() {
      7. fmt.Println("开始执行主函数")
      8. }
    • 如果一个文件中同时包含全局变量init函数main函数,则执行顺序为:全局变量 > init > main
      1. package main
      2. import "fmt"
      3. var money = test()
      4. func test() int {
      5. fmt.Println("test()")
      6. return 100
      7. }
      8. func init() {
      9. fmt.Println("init函数自动执行")
      10. }
      11. func main() {
      12. fmt.Println("开始执行主函数...money=", money)
      13. }
      14. // test()
      15. // init函数自动执行
      16. // 开始执行主函数...money= 100

    7.匿名函数

    Go支持匿名函数,如果我们某个函数只希望使用一次,可以考虑使用匿名函数,匿名函数可以实现多次调用

    • 示例一:在定义时就调用
      1. package main
      2. import "fmt"
      3. func main() {
      4. res := func(n1, n2 int) int {
      5. return n1 + n2
      6. }(10, 20)
      7. fmt.Println("result =", res)
      8. }
    • 示例二:将匿名函数赋值给一个变量(函数变量),再通过该变量来调用匿名函数
      1. package main
      2. import "fmt"
      3. func main() {
      4. sum := func(n1, n2 int) int {
      5. return n1 + n2
      6. }
      7. res := sum(10, 20)
      8. fmt.Println("result =", res)
      9. }
    • 示例三:全匿名函数,将函数赋值给一个全局变量,就成为一个全局函数,可以在程序有效
      1. package main
      2. import "fmt"
      3. var multi = func(n1, n2 int) int {
      4. return n1 * n2
      5. }
      6. func main() {
      7. res := multi(10, 20)
      8. fmt.Println("result =", res)
      9. }

    8.闭包

    • 闭包就是一个函数与其相关的引用环境组合的一个整体

    • 闭包必须满足三个条件:
      1、必须有一个内嵌函数
      2、内嵌函数必须引用外部函数中的变量
      3、外部函数返回值必须是内嵌函数

    • 闭包可以使得变量常驻内存

      1. package main
      2. import "fmt"
      3. func Closure() func(int) int {
      4. var n1 = 10
      5. return func(n2 int) int {
      6. n1 = n1 + n2
      7. return n1
      8. // return n1 + n2 // 这样的结果完全不一样, n1 = n1 + n2会修改局部变量n1的值,如果直接返回n1 + n2,则不会修改局部变量n1的值
      9. }
      10. }
      11. func main() {
      12. res := Closure()
      13. fmt.Println("result =", res(30)) // 40
      14. fmt.Println("result =", res(31)) // 71
      15. }
    • nodejs

      1. function closure() {
      2. var a = 10;
      3. function inner(b) {
      4. a = a + b;
      5. return a
      6. }
      7. return inner;
      8. }
      9. var func = closure();
      10. console.log(func(30)); // 40
      11. console.log(func(31)); // 71
    • python

      1. # 变量 a 对于 inner来说是外部变量,因此不能直接进行修改
      2. def closure():
      3. a = 10
      4. def inner(b):
      5. a = a + b
      6. return a
      7. return inner
      8. # UnboundLocalError: local variable 'a' referenced before assignment
      9. # 但对于inner来说是可以直接使用的
      10. def closure():
      11. a = 10
      12. def inner(b):
      13. return a + b
      14. return inner
      15. # 必须使用 nonlocal 来修改变量a的作用域,从而对其进行操做
      16. def closure():
      17. a = 10
      18. def inner(b):
      19. nonlocal a
      20. a = a + b
      21. return a
      22. return inner
      23. func = closure()
      24. print(func(30)) # 40
      25. print(func(31)) # 71
    • 案例:使用闭包方式,实现检查文件后缀名是否为.jpg,如果不是,则内部实现拼接.jpg,否则直接返回

      1. package main
      2. import (
      3. "fmt"
      4. "strings"
      5. )
      6. func Closure(endFix string) func(string) string {
      7. return func(name string) string {
      8. if strings.HasSuffix(name, endFix) {
      9. return name
      10. }
      11. return name + endFix
      12. }
      13. }
      14. func main() {
      15. res := Closure(".jpg")
      16. fmt.Println("result =", res("aaa.jpg"))
      17. fmt.Println("result =", res("bbb"))
      18. }

    9.函数参数传递

    • 值类型:基本数据类型:int系列、float系列、bool、string、数组、结构体
    • 引用类型:指针、slice切片、map、管道chan、interface等

     结构体

    结构体是一种用户自定义的数据类型,它由一组字段组成,每个字段可以是任意基本类型或其他结构体类型。结构体在Go语言中被广泛使用,它可以用来表示复杂的数据结构,比如二叉树、链表、图等。

    一个简单的结构体定义如下:

    1. copy codetype Person struct {
    2. name string
    3. age int
    4. gender string
    5. }

    上面的代码定义了一个名为Person的结构体,它有三个字段,分别是name、age和gender,它们的类型分别为string、int和string。我们可以通过该结构体来表示一个人的基本信息。

    1.结构体的语法

    Go语言中结构体的语法非常简洁,它的基本形式如下:

    1. copy codetype 结构体名 struct {
    2. 字段1 类型1
    3. 字段2 类型2
    4. ...
    5. 字段n 类型n
    6. }

    其中,结构体名是用户自定义的标识符,用于表示该结构体的类型。字段是结构体中的成员,每个字段都有一个对应的类型。在定义结构体时,可以使用任何基本类型或其他结构体类型作为字段的类型。另外,结构体中的字段可以使用大写字母开头的标识符表示公有成员,小写字母开头的标识符表示私有成员。

    2.结构体的初始化

    在Go语言中,结构体类型的变量可以通过结构体字面量来进行初始化。结构体字面量是一种简洁的语法,可以用于快速创建一个结构体类型的变量。

    结构体字面量的基本形式如下:

    1. copy codevar 变量名 = 结构体类型{
    2. 字段1: 值1,
    3. 字段2: 值2,
    4. ...
    5. 字段n: 值n,
    6. }

    其中,变量名是结构体类型的变量名,结构体类型是用户自定义的结构体类型,字段是结构体中的成员,值是该成员对应的初始化值。我们可以省略字段名,只提供值,此时Go语言会按照结构体定义时的字段顺序来进行初始化。例如:

    1. copy codevar p1 = Person{
    2. "张三",
    3. 18,
    4. "男",
    5. }
    6. var p2 = Person{
    7. name: "李四",
    8. age: 20,
    9. gender: "女",
    10. }

    上面的代码分别创建了两个Person类型的变量p1和p2,它们的字段值分别为{张三 18 男}和{李四 20 女}。可以看出,结构体字面量提供了一种简单、直观的方式来初始化结构体类型的变量。

    3.结构体的访问

    在Go语言中,可以通过结构体变量的字段名来访问结构体中的成员。例如:

    1. copy codevar p Person
    2. p.name = "张三"
    3. p.age = 18
    4. p.gender = "男"

    上面的代码创建了一个名为p的Person类型的变量,并对其成员进行了赋值。可以看出,访问结构体成员的语法非常简单,只需要使用点号(.)连接结构体变量和成员名即可。

    4.结构体的方法

    Go语言中的结构体还支持方法,方法是一种特殊的函数,它可以与结构体类型关联。方法与函数的区别在于,方法必须在结构体类型上定义,而函数可以在任何地方定义。另外,方法可以访问结构体的成员,而函数只能访问其参数。

    Go语言中的方法定义如下:

    1. copy codefunc (接收者变量 接收者类型) 方法名(参数列表) 返回值列表 {
    2. // 方法体
    3. }

    其中,接收者变量是一个标识符,用于表示方法所属的结构体类型的变量。接收者类型是方法所属的结构体类型,参数列表和返回值列表与普通函数的定义方式相同。

    下面是一个例子:

    1. copy codetype Rectangle struct {
    2. width, height float64
    3. }
    4. func (r Rectangle) Area() float64 {
    5. return r.width * r.height
    6. }
    7. func main() {
    8. r := Rectangle{3, 4}
    9. fmt.Println("面积:", r.Area())
    10. }

    上面的代码定义了一个名为Rectangle的结构体,它有两个字段width和height。该结构体还定义了一个名为Area的方法,用于计算矩形的面积。

    在main函数中,创建了一个Rectangle类型的变量r,并调用其Area方法来计算面积。

    5.结构体的嵌套

    在Go语言中,结构体可以嵌套在其他结构体中,从而形成更复杂的数据结构。嵌套结构体的定义方式与普通结构体的定义方式相同,只是在字段的类型中指定另一个结构体类型。

    下面是一个例子:

    1. copy codetype Address struct {
    2. Province string
    3. City string
    4. }
    5. type Person struct {
    6. Name string
    7. Age int
    8. Address Address
    9. }
    10. func main() {
    11. p := Person{
    12. Name: "张三",
    13. Age: 18,
    14. Address: Address{
    15. Province: "广东省",
    16. City: "深圳市",
    17. },
    18. }
    19. fmt.Println(p)
    20. }

    上面的代码定义了两个结构体类型,Address和Person。Person结构体中包含一个Address类型的字段,用于表示该人员的地址信息。

    在main函数中,创建了一个Person类型的变量p,并对其成员进行了赋值。可以看出,嵌套结构体提供了一种简单、灵活的方式来表示复杂的数据结构。

    6.结构体的匿名字段

    在Go语言中,结构体还支持匿名字段。匿名字段是一种特殊的字段类型,它没有字段名,只有字段类型。在结构体中,可以使用匿名字段来表示继承关系或者组合关系。

    下面是一个例子:

    1. copy codetype Person struct {
    2. string
    3. int
    4. }
    5. func main() {
    6. p := Person{"张三", 18}
    7. fmt.Println(p.string, p.int)
    8. }

    上面的代码定义了一个Person结构体,它包含了一个string类型的匿名字段和一个int类型的匿名字段。在main函数中,创建了一个Person类型的变量p,并对其成员进行了访问。可以看出,使用匿名字段可以在一定程度上简化代码。

    7.结构体的标签

    在Go语言中,结构体的字段可以使用标签(tag)来进行注释或者元数据的传递。标签是一个字符串,它的格式为key:"value",其中key表示标签的名称,value表示标签的值。

    下面是一个例子:

    1. copy codetype Person struct {
    2. Name string `json:"name"`
    3. Age int `json:"age"`
    4. }
    5. func main() {
    6. p := Person{"张三", 18}
    7. b, _ := json.Marshal(p)
    8. fmt.Println(string(b))
    9. }

    上面的代码定义了一个Person结构体,它有两个字段Name和Age。在Name字段和Age字段上,我们使用了json标签,用于指定json序列化时使用的字段名。

    在main函数中,创建了一个Person类型的变量p,并将其序列化为json字符串。可以看出,使用标签可以方便地进行元数据传递和注释。

    8.结构体的应用场景

    在Go语言中,结构体被广泛应用于各种数据结构的定义和实现。下面是一些常见的应用场景:

    • 数据库ORM:结构体可以与数据库表进行映射,从而实现对象关系映射(ORM)功能。
    • 网络编程:结构体可以用于表示网络数据包的格式,从而实现网络编程功能。
    • 并发编程:结构体可以用于实现并发编程模型,例如管道、锁和条件变量等。
    • 配置文件:结构体可以用于表示配置文件的格式,从而实现配置文件读写功能。
    • 模板引擎:结构体可以用于表示模板数据,从而实现模板引擎功能。

  • 相关阅读:
    win10 上安装 pytorch + cuda
    3、后台数据库连接以及工具类编写 [木字楠博客]
    walking机器人仿真教程-深度学习-使用yolov5进行物体识别
    alpha-beta剪枝
    27.4 Java集合之Map学习
    今天虚竹哥又发现了一款好用的国产化API工具
    汇川IT7000系列HMI使用脚本实现画面跳转时自动切换手自动模式
    Oracle SQL执行计划操作(7)——排序相关操作
    QT+OSG/osgEarth编译之三十二:Exiv2+Qt编译(一套代码、一套框架,跨平台编译,版本:Exiv2-0.27.5)
    第三节:kafka sarama 遇到Bug?
  • 原文地址:https://blog.csdn.net/Lyon_yong/article/details/136606844