go语言基础笔记

时间:2024-03-17 16:30:11

1.基本类型

1.1. 基本类型

    bool

    int: int8, int16, int32(rune), int64

    uint: uint8(byte), uint16, uint32, uint64

    float32, float64

    string

    复数:complex64, complex128

    复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。

    array    -- 固定长度的数组

    

int8 range: -128 127

int16 range: -32768 32767

int32 range: -2147483648 2147483647

int64 range: -9223372036854775808 9223372036854775807

int32: 0x3e6f54ff 1047483647

int16: 0x54ff 21759

 // 输出各数值范围

 fmt.Println("int8 range:", math.MinInt8, math.MaxInt8)

注意:

    byte // uint8 的别名

    rune // int32 的别名 代表一个 Unicode 码

    int  代表  int64

    float 代表  float64

    Go 语言中不允许将整型强制转换为布尔型.,布尔型无法参与数值运算,也无法与其他类型进行转换。

    当一个变量被声明之后,系统自动赋予它该类型的零值: int 为 0,float 为 0.0,bool 为 false,string 为空字符串

    切片slice、map、channel、interface、function的默认为 nil

 

1.2 值类型和引用类型:

值类型:int、float、bool、string、数组array、结构体struct

引用类型:指针、切片slice、map、接口interface、函数func、管道chan

区别:

1)值类型:变量直接存储值,内存通常在栈中分配。

给新的变量赋值时(拷贝时),为拷贝,直接开辟新的内存地址存储值。

2)引用类型:

变量直接存储内存地址,这个地址存储值。内存通常再堆上分配。

给新的变量赋值时(拷贝时),为浅拷贝,新的变量通过指针指向原来的内存地址。可以使用copy关键字实现引用类型的深拷贝。

当如果没有任何一个变量引用这个地址时,这个地址就会被GC垃圾回收。

2.变量

2.1变量的声明

1)var 变量名 类型 = 表达式

如:var a int = 27

如果变量没有初始化默认为对应类型的初始值

2)如果变量没有指定类型可以通过变量的初始值来判断变量类型

 var d = true

3)使用 :=

使用格式:名称 :=

也就是说a := 1相等于:

var a int

a =1

2.2多变量声明

可以同时声明多个类型相同的变量(非全局变量),如下图所示:

var x, y int

var c, d int = 1, 2

g, h := 123, "hello"

关于全局变量的声明如下:

var (

    a int

    b bool

)

2.3匿名变量

匿名变量的特点是一个下画线_,这本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。

使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。

示例代码如下:

    func GetData() (int, int) {

  

        return 10, 20

    }

    func main(){

  

        a, _ := GetData()

        _, b := GetData()

        fmt.Println(a, b)

    }

需要注意的是匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。

2.4 变量作用域

根据定义位置的不同,可以分为一下三个类型:

  • 局部变量
  • 全局变量以 var 关键字开头外部包中使用首字母必须大写
  • 形式参数形式参数会作为函数的局部变量来使用

3.数组

3.1一维数组:

    全局:

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

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

    var arr2 = [...]int{1, 2, 3, 4, 5, 6}

    var str = [5]string{3: "hello world", 4: "tom"}

    局部:

    a := [3]int{1, 2}           // 未初始化元素值为 0。

    b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。

    c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。

    d := [...]struct {

        name string

        age  uint8

    }{

        {"user1", 10}, // 可省略元素类型。

        {"user2", 20}, // 别忘了最后一行的逗号。

    }

代码:

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

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

var arr2 = [...]int{1, 2, 3, 4, 5, 6}

var str = [5]string{3: "hello world", 4: "tom"}

func main() {

    a := [3]int{1, 2}           // 未初始化元素值为 0。

    b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。

    c := [5]int{2: 100, 4: 200} // 使用引号初始化元素。

    d := [...]struct {

        name string

        age  uint8

    }{

        {"user1", 10}, // 可省略元素类型。

        {"user2", 20}, // 别忘了最后一行的逗号。

    }

    fmt.Println(arr0, arr1, arr2, str)

    fmt.Println(a, b, c, d)

}

输出结果:

[1 2 3 0 0] [1 2 3 4 5] [1 2 3 4 5 6] [   hello world tom]

[1 2 0] [1 2 3 4] [0 0 100 0 200] [{user1 10} {user2 20}]

3.2多维数组

    全局

    var arr0 [5][3]int

    var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

    局部:

    a := [2][3]int{{1, 2, 3}, {4, 5, 6}}

    b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。

代码:

var arr0 [5][3]int

var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

func main() {

    a := [2][3]int{{1, 2, 3}, {4, 5, 6}}

    b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。

    fmt.Println(arr0, arr1)

    fmt.Println(a, b)

}

输出结果:

    [[0 0 0] [0 0 0] [0 0 0] [0 0 0] [0 0 0]] [[1 2 3] [7 8 9]]

    [[1 2 3] [4 5 6]] [[1 1] [2 2] [3 3]]

内置函数 len 和 cap 都返回数组长度 (元素数量)。如:

a := [2]int{}

println(len(a), cap(a))  //得到 2    2

3.3多维数组遍历:

func main() {

    var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

    for k1, v1 := range f {

        for k2, v2 := range v1 {

            fmt.Printf("(%d,%d)=%d ", k1, k2, v2)

        }

        fmt.Println()

    }

}

输出结果:

  (0,0)=1 (0,1)=2 (0,2)=3

  (1,0)=7 (1,1)=8 (1,2)=9

3.4. 数组拷贝和传参

package main

import "fmt"

func printArr(arr *[5]int) {

    arr[0] = 10

    for i, v := range arr {

        fmt.Println(i, v)

    }

}

func main() {

    var arr1 [5]int

    printArr(&arr1)

    fmt.Println(arr1)

    arr2 := [...]int{2, 4, 6, 8, 10}

    printArr(&arr2)

    fmt.Println(arr2)

}

4.切片Slice

需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

    1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。

    2. 切片的长度可以改变,因此,切片是一个可变的数组。

    3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。

    4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。

    5. 切片的定义:var 变量名 []类型,比如 var str []string  var arr []int。

    6. 如果 slice == nil,那么 len、cap 结果都等于 0。

4.1 创建切片的各种方式

package main

import "fmt"

func main() {

   //1.声明切片

   var s1 []int

   if s1 == nil {

      fmt.Println("是空")

   } else {

      fmt.Println("不是空")

   }

   // 2.:=

   s2 := []int{}

   // 3.make()

   var s3 []int = make([]int, 0)

   fmt.Println(s1, s2, s3)

   // 4.初始化赋值

   var s4 []int = make([]int, 0, 0)

   fmt.Println(s4)

   s5 := []int{1, 2, 3}

   fmt.Println(s5)

   // 5.从数组切片

   arr := [5]int{1, 2, 3, 4, 5}

   var s6 []int

   // 前包后不包

   s6 = arr[1:4]

   fmt.Println(s6)

}

4.2. 切片初始化

全局:

var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

var slice0 []int = arr[start:end]

var slice1 []int = arr[:end]        

var slice2 []int = arr[start:]        

var slice3 []int = arr[:]

var slice4 = arr[:len(arr)-1]      //去掉切片的最后一个元素

局部:

arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}

slice5 := arr[start:end]

slice6 := arr[:end]        

slice7 := arr[start:]     

slice8 := arr[:]  

slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素

代码:

package main

import (

    "fmt"

)

var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

var slice0 []int = arr[2:8]

var slice1 []int = arr[0:6]        //可以简写为 var slice []int = arr[:end]

var slice2 []int = arr[5:10]       //可以简写为 var slice[]int = arr[start:]

var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:]

var slice4 = arr[:len(arr)-1]      //去掉切片的最后一个元素

func main() {

    fmt.Printf("全局变量:arr %v\n", arr)

    fmt.Printf("全局变量:slice0 %v\n", slice0)

    fmt.Printf("全局变量:slice1 %v\n", slice1)

    fmt.Printf("全局变量:slice2 %v\n", slice2)

    fmt.Printf("全局变量:slice3 %v\n", slice3)

    fmt.Printf("全局变量:slice4 %v\n", slice4)

    fmt.Printf("-----------------------------------\n")

    arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}

    slice5 := arr[2:8]

    slice6 := arr[0:6]         //可以简写为 slice := arr[:end]

    slice7 := arr[5:10]        //可以简写为 slice := arr[start:]

    slice8 := arr[0:len(arr)]  //slice := arr[:]

    slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素

    fmt.Printf("局部变量: arr2 %v\n", arr2)

    fmt.Printf("局部变量: slice5 %v\n", slice5)

    fmt.Printf("局部变量: slice6 %v\n", slice6)

    fmt.Printf("局部变量: slice7 %v\n", slice7)

    fmt.Printf("局部变量: slice8 %v\n", slice8)

    fmt.Printf("局部变量: slice9 %v\n", slice9)

}

输出结果:

    全局变量:arr [0 1 2 3 4 5 6 7 8 9]

    全局变量:slice0 [2 3 4 5 6 7]

    全局变量:slice1 [0 1 2 3 4 5]

    全局变量:slice2 [5 6 7 8 9]

    全局变量:slice3 [0 1 2 3 4 5 6 7 8 9]

    全局变量:slice4 [0 1 2 3 4 5 6 7 8]

    -----------------------------------

    局部变量: arr2 [9 8 7 6 5 4 3 2 1 0]

    局部变量: slice5 [2 3 4 5 6 7]

    局部变量: slice6 [0 1 2 3 4 5]

    局部变量: slice7 [5 6 7 8 9]

    局部变量: slice8 [0 1 2 3 4 5 6 7 8 9]

    局部变量: slice9 [0 1 2 3 4 5 6 7 8]

4.3. 通过make来创建切片

    var slice []type = make([]type, len)

    slice  := make([]type, len)

    slice  := make([]type, len, cap)

使用 make 动态创建slice,避免了数组必须用常量做长度的麻烦。还可用指针直接访问底层数组,退化成普通数组操作。

package main

import "fmt"

func main() {

    s := []int{0, 1, 2, 3}

    p := &s[2] // *int, 获取底层数组元素指针。

    *p += 100

    fmt.Println(s)

}

输出结果:

    [0 1 102 3]

至于 [][]T,是指元素类型为 []T 。

data := [][]int{

        []int{1, 2, 3},

        []int{100, 200},

        []int{11, 22, 33, 44},

    }

    fmt.Println(data)

输出结果:

    [[1 2 3] [100 200] [11 22 33 44]]

4.4. 用append内置函数操作切片(切片追加)

package main

import (

    "fmt"

)

func main() {

    var a = []int{1, 2, 3}

    fmt.Printf("slice a : %v\n", a)

    var b = []int{4, 5, 6}

    fmt.Printf("slice b : %v\n", b)

    c := append(a, b...)

    fmt.Printf("slice c : %v\n", c)

    d := append(c, 7)

    fmt.Printf("slice d : %v\n", d)

    e := append(d, 8, 9, 10)

    fmt.Printf("slice e : %v\n", e)

}

输出结果:

    slice a : [1 2 3]

    slice b : [4 5 6]

    slice c : [1 2 3 4 5 6]

    slice d : [1 2 3 4 5 6 7]

    slice e : [1 2 3 4 5 6 7 8 9 10]

append :向 slice 尾部添加数据,返回新的 slice 对象。

package main

import (

    "fmt"

)

func main() {

    s1 := make([]int, 0, 5)

    fmt.Printf("%p\n", &s1) //0xc42000a060

    s2 := append(s1, 1)

    fmt.Printf("%p\n", &s2)//0xc42000a080

    fmt.Println(s1, s2) //[] [1]

}

4.5. 超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。

func main() {

    data := [...]int{0, 1, 2, 3, 4, 10: 0}

    s := data[:2:3]

    s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。

    fmt.Println(s, data)         // 重新分配底层数组,与原数组无关。

    fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。

   //得到 [0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]

    //0xc4200160f0 0xc420070060

}

从输出结果可以看出,append 后的 s 重新分配了底层数组,并复制数据。如果只追加一个值,则不会超过 s.cap 限制,也就不会重新分配。 通常以 2 倍容量重新分配底层数组。在大批量添加数据时,建议一次性分配足够大的空间,以减少内存分配和数据复制开销。或初始化足够长的 len 属性,改用索引号进行操作。及时释放不再使用的 slice 对象,避免持有过期数组,造成 GC 无法回收。

4.6. slice中cap重新分配规律:

package main

import (

    "fmt"

)

func main() {

    s := make([]int, 0, 1)

    c := cap(s)

    for i := 0; i < 50; i++ {

        s = append(s, i)

        if n := cap(s); n > c {

            fmt.Printf("cap: %d -> %d\n", c, n)

            c = n

        }

    }

}

输出结果:

    cap: 1 -> 2

    cap: 2 -> 4

    cap: 4 -> 8

    cap: 8 -> 16

    cap: 16 -> 32

    cap: 32 -> 64

4.7. 切片拷贝

package main

import (

    "fmt"

)

func main() {

    s1 := []int{1, 2, 3, 4, 5}

    fmt.Printf("slice s1 : %v\n", s1)

    s2 := make([]int, 10)

    fmt.Printf("slice s2 : %v\n", s2)

    copy(s2, s1)

    fmt.Printf("copied slice s1 : %v\n", s1)

    fmt.Printf("copied slice s2 : %v\n", s2)

    s3 := []int{1, 2, 3}

    fmt.Printf("slice s3 : %v\n", s3)

    s3 = append(s3, s2...)

    fmt.Printf("appended slice s3 : %v\n", s3)

    s3 = append(s3, 4, 5, 6)

    fmt.Printf("last slice s3 : %v\n", s3)

}

输出结果:

    slice s1 : [1 2 3 4 5]

    slice s2 : [0 0 0 0 0 0 0 0 0 0]

    copied slice s1 : [1 2 3 4 5]

    copied slice s2 : [1 2 3 4 5 0 0 0 0 0]

    slice s3 : [1 2 3]

    appended slice s3 : [1 2 3 1 2 3 4 5 0 0 0 0 0]

    last slice s3 : [1 2 3 1 2 3 4 5 0 0 0 0 0 4 5 6]

copy :函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准。两个 slice 可指向同一底层数组,允许元素区间重叠。

package main

import (

    "fmt"

)

func main() {

    data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

    fmt.Println("array data : ", data)

    s1 := data[8:]

    s2 := data[:5]

    fmt.Printf("slice s1 : %v\n", s1)

    fmt.Printf("slice s2 : %v\n", s2)

    copy(s2, s1)

    fmt.Printf("copied slice s1 : %v\n", s1)

    fmt.Printf("copied slice s2 : %v\n", s2)

    fmt.Println("last array data : ", data)

}

输出结果:

    array data :  [0 1 2 3 4 5 6 7 8 9]

    slice s1 : [8 9]

    slice s2 : [0 1 2 3 4]

    copied slice s1 : [8 9]

    copied slice s2 : [8 9 2 3 4]

    last array data :  [8 9 2 3 4 5 6 7 8 9]

应及时将所需数据 copy 到较小的 slice,以便释放超大号底层数组内存。

4.8. slice遍历: