golang:函数 & 数组 & 切片 & 锁

内置函数

// 1. close:主要用来关闭channel
// 2. len:用来求长度,比如string、array、slice、map、channel
// 3. new:用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
// 4. make:用来分配内存,主要用来分配引用类型,比如chan、map、slice
// 5. append:用来追加元素到数组、slice中

示例如下:

// new 示例:
package main

import "fmt"

func main(){
    var i int
    fmt.Println(i)

    j := new(int)
    fmt.Println(j)
    
    *j = 100
    fmt.Println(*j)
}

// 运行结果如下: 
[root@NEO example01_new]# go run main/main.go 
0
0xc0000160b0
100
[root@NEO example01_new]#

// append 示例:
package main

import "fmt"

func main(){
    var a []int    // [] 中不写数字表示是一个切片 slice
    a = append(a,1,2)    // append 中也是可变参数
    fmt.Println(a)

    a = append(a,a...)    // 添加一个切片;a...表示 切片a 中的所有元素
    fmt.Println(a)
}

// 运行结果如下:
[root@NEO example01_append]# go run main/main.go
[1 2]
[1 2 1 2]
[root@NEO example01_append]# 

// recover 示例:
package main

import "fmt"

func test(){
    defer func(){   // func(){} 是匿名函数; func(){...}()  执行匿名函数
    if err := recover(); err != nil{    // recover()  是捕获异常
        fmt.Println(err)
    }
    }()

    a := 0
    b := 1/a
    fmt.Println(b)
    return
}

func main(){
    test()
    fmt.Println("hello world")
}

// 运行结果如下:
[root@NEO example01_recover]# go run main/main.go
runtime error: integer divide by zero
hello world
[root@NEO example01_recover]# 

递归函数

一个函数调用自己,就叫做递归。示例代码如下:

// 示例代码1:
package main

import (
    "fmt"
)

func calc(n int) int {
    if n == 1 {
        return 1
    }

    return calc(n-1) * n
}

func main() {
    n := calc(5)
    fmt.Println(n)
}


// 示例2:斐波那契数
package main

import "fmt"

func fab(n int) int {
    if n <= 1 {
    return 1
    }
    return fab(n-1)+fab(n-2)
}

func main(){
    n := fab(10)
    fmt.Println(n)
}

递归的设计原则

1)一个大的问题能够分解成相似的小问题
2)定义好出口条件

闭包

闭包:一个函数和与其相关的引用环境组合而成的实体。示例如下:

// 示例代码:
package main

import "fmt"

func Adder() func(int) int {    // 函数 Adder 的返回值是也一个函数
    var x int
    return func(d int) int {    // 该处的 return 要和上面定义的返回值类型的 return 的函数一样
    x += d
    return x
    }
}

func main() {
    f := Adder()   // f 是一个函数
    fmt.Println("f(1)",f(1))         // 此时 x 的值为默认的 0
    fmt.Println("f(10)",f(10))    // 此时 x 的值变成了 1
    fmt.Println("f(100)",f(100))    // 此时 x 的值变成了 10
}

// 运行结果如下:
[root@NEO example03_closure]# go run main/main.go
f(1) 1
f(10) 11
f(100) 111
[root@NEO example03_closure]#

数组和切片

// 1. 数组:是同一种数据类型的固定长度的序列。
// 2. 数组定义:var a [len]int,比如:var a[5]int,一旦定义,长度不能变;元素的值默认为0
// 3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型
// 4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
       for i := 0; i < len(a); i++ {
       }
       
       for index, v := range a {
       }

// 5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
// 6. 数组是值类型,因此改变副本的值,不会改变本身的值
    arr2 := arr1
    arr2[2] = 100

数组的初始化

1. var age0 [5]int = [5]int{1,2,3}        // 定义的时候初始化
2. var age1 = [5]int{1,2,3,4,5}
3. var age2 = […]int{1,2,3,4,5,6}        // ... 的作用:编译器会自动帮你数里面有几个元素,然后就是长度为多少的数组
4. var str = [5]string{3:”hello world”, 4:”tom”}    // 定义时指定下标对应的元素

多维数组

1. var age [5][3]int        // 二维数组:可理解成5行3列
2. var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}        // ... 表示 编译器自己去数有多少行

多维数组遍历

// 示例代码:
package main

import "fmt"

func test(){
    var arr [2][5]int = [...][5]int{{1,2,3,4,5},{6,7,8,9,10}}

    for row,v_row := range arr{
    for col,v_col := range v_row {
        fmt.Printf("(%d,%d)=%d ",row,col,v_col)
    }
    fmt.Println()
    }
}

func main(){
    test()
}


// 运行结果如下:
[root@NEO example04_multi_dim_arr]# go run main/main.go
(0,0)=1 (0,1)=2 (0,2)=3 (0,3)=4 (0,4)=5 
(1,0)=6 (1,1)=7 (1,2)=8 (1,3)=9 (1,4)=10 
[root@NEO example04_multi_dim_arr]#

切片

// 1. 切片:切片是数组的一个引用,因此切片是引用类型;把一个切片传入函数中修改切片中元素的值,那么该切片在函数外的值也会改变,数组则不会
// 2. 切片的长度可以改变,因此,切片是一个可变的数组
// 3. 切片遍历方式和数组一样,并且可以用len()求长度
// 4. cap可以求出slice最大的容量,0 <= len(slice) <= cap(array),其中array是slice引用的数组
// 5. 切片的定义:var 变量名 []类型,比如 var str []string 和 var arr []int (这种方式只是定义了切片,但并没有对切片初始化,所以此时不能直接 arr[0]=1 这样赋值)

示例如下:

// 切片是引用类型的示例1:
package main

import "fmt"

func modifySlice(a []int){    // 该函数的需要传入的参数是切片类型;slice map 和 chan 类型的参数传入的就是地址
    a[1] = 100        // 修改 切片 a 的元素的值后,函数外 a 的值也会改变
}

func testSlice(){
    var s []int = []int{1,2,3}
    modifySlice(s)    // 切片是引用类型,是数组的一个引用(可理解成切片是一个地址);此函数传了一个引用(引用中包含指针);可理解成给该函数传了一个地址
    fmt.Println(s)
}

func main(){
    testSlice()
}


// 运行结果如下:
[root@NEO example05_slice_ref_type]# go run main/main.go
[1 100 3]        // 函数外切片的值也发生了改变
[root@NEO example05_slice_ref_type]#

// 切片是引用类型的示例2:
package main

import "fmt"

func testSliceRefArr(){
    var a = [10]int{1,2,3}    // 声明并初始化一个数组
    b := a[1:4]        // 声明并初始化一个切片

    fmt.Printf("%p\n",b)    // %p 表示十六进制表示,可用于打印一个地址
    fmt.Printf("%p\n",&a[1])    // 数组 a 第一个元素的地址
}

func main(){
    testSliceRefArr()
}

// 运行结果如下:
[root@NEO example05_slice_ref_arr]# go run main/main.go
0xc000064008
0xc000064008    // 切片 b  的地址和 数组a第一个元素的地址是一样的;所以 切片是一个地址
[root@NEO example05_slice_ref_arr]# 

// 示例代码:
package main

import "fmt"

func testSlice(){
    var slice []int    // 声明一个数组;只是定义了数组,但并没有对切片初始化,所以此时不能直接 arr[0]=1 这样赋值
    var arr [5]int = [...]int{1,2,3,4,5}    // 声明并初始化一个数组

    slice = arr[2:5]    // 对切片初始化; 顾首不顾尾;可简写成 arr[2:]
    fmt.Println(slice)
    fmt.Println("len:",len(slice))
    fmt.Println("cap:",cap(slice))

    slice = slice[0:1]
    fmt.Println("len:",len(slice))
    fmt.Println("cap:",cap(slice))
}

func main(){
    testSlice()
}
// slice、map、channel 都可以用 make 来初始化

// 运行结果如下:
[root@NEO example05_slice01]# go run main/main.go
[3 4 5]
len: 3
cap: 3
len: 1
cap: 3
[root@NEO example05_slice01]#

切片的创建方式1:通过 数组 创建

// 1. 切片初始化:var slice []int = arr[start:end] ;包含start到end之间的元素,但不包含end 
// 2. var slice []int = arr[0:end]可以简写为 var slice []int=arr[:end]
// 3. var slice []int = arr[start:len(arr)] 可以简写为 var slice[]int = arr[start:]
// 4. Var slice []int = arr[0, len(arr)] 可以简写为 var slice[]int = arr[:]
// 5. 如果要切片最后一个元素去掉,可以这么写: 
       Slice = slice[:len(slice)-1]

切片的创建方式2: 通过make来创建切片

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

用append内置函数操作切片

扫描二维码关注公众号,回复: 6851174 查看本文章
// 示例:
slice = append(slice, 10)
var a = []int{1,2,3}
var b = []int{4,5,6}
a = append(a, b…)    // b... 表示 切片 b 中的所有元素


// append 的示例:
package main

import "fmt"

func testSlice(){
    var a [5]int = [...]int{1,2,3,4,5}
    b := a[1:]

    fmt.Printf("b=%p a[1]=%p\n",b,&a[1])   // 此时切片 b 的地址和 数组a第一个元素的地址是一样的

    b[1] = 100
    fmt.Println("before a:",a)    // 此时 改变切片 b 的值 数组a 也会改变

    b = append(b,10)
    b = append(b,10)
    b = append(b,10)
    b = append(b,10)
    b = append(b,10)
    b = append(b,10)    // 添加了5个元素后,已经超过了切片 b 的容量

    fmt.Println(b)
    fmt.Printf("b=%p a[1]=%p\n",b,&a[1])    // 此时切片 b 的地址和 数组a第一个元素的地址就不再一样;因为在声明并定义切片b时没有超过b的容量,此时b指向原来的数组,当超过b的容量后,系统会重新开辟一块内存,并把切片b原有的值放到新开辟的内存中,然再把追加的内容放进去
    
    b[1] = 1000
    fmt.Println("after a:",a)    // 此时改变 b 值, a的值将不会改变,因为 切片 b 指向的内存地址已经不是 a 了
}

func main(){
    testSlice()       // 切片是可变的,就是因为内部会根据容量的大小重新分配内存
}

// 运行结果如下:
[root@NEO example05_slice_beyond_cap]# go run main/main.go
b=0xc000014158 a[1]=0xc000014158
before a: [1 2 100 4 5]                // a 中对应的值也发生了改变
[2 100 4 5 10 10 10 10 10 10]
b=0xc000062080 a[1]=0xc000014158
after a: [1 2 100 4 5]                // 此时 a 中的值并没有改变
[root@NEO example05_slice_beyond_cap]# 

切片resize

var a = []int {1,3,4,5}
b := a[1:2]
b = b[0:3]

切片拷贝:copy()

s1 := []int{1,2,3,4,5}
s2 := make([]int, 10)
copy(s2, s1)    // 把 切片 s1 拷贝到 切片 s2 中;拷贝不会扩容

// 示例代码:
package main

import "fmt"

func testCopy(){
    var a []int = []int{1,2,3,4,5}
    b := make([]int,10)
    c := make([]int,1)

    copy(b,a)    // 把a拷贝到b中
    copy(c,a)    // 把a拷贝到c中

    fmt.Println(b)
    fmt.Println(c)    // 拷贝不会扩容
}

func main(){
    testCopy()
}

// 运行结果如下:
[root@NEO example05_slice_copy]# go run main/main.go
[1 2 3 4 5 0 0 0 0 0]
[1]
[root@NEO example05_slice_copy]# 

切片示例:使用非递归的方式实现斐波那契数列,打印前10个数。

// 示例代码如下:
package main

import "fmt"

func fab(n int){
    var a []int           // 声明一个切片;[]中没有数字,所以是切片
    a = make([]int,n)    // 为切片a 分配内存

    a[0] = 1
    a[1] = 1

    for i := 2; i < len(a); i++ {
    a[i] = a[i-1] + a[i-2]
    }
    
    for _,v := range a{
    fmt.Println(v)
    }
}

func main() {
    fab(10)
}

// 运行结果如下:
[root@NEO example04_fab_slice]# go run main/main.go
1
1
2
3
5
8
13
21
34
55
[root@NEO example04_fab_slice]#

猜你喜欢

转载自www.cnblogs.com/neozheng/p/11235338.html