GO-指針與函數


一、指針類型

1、普通類型,變量存的就是值,也叫值類型。指針類型存的是地址

2、獲取變量的地址,用&,比如:var a int, 獲取a的地址 &a

3、指針類型,變量存的是一個地址,這個地址存的才是值

4、獲取指針類型所指向的值,使用:* ,比如:var p *int,使用*p獲取p指向的變量的值

var a int = 5 

var p *int = &a   0xefefefe 指向變量a的值是 5

5、指針類型的變量初始話有兩種:

5.1、直接給指針賦值其他變量的地址

func test3(){
    var p *int  //默認初始化nil
    var a int
    p = &a
    *p = 200
    fmt.Println(a)
}

5.2、使用new分配

func test4(){
    var p *int
    p = new(int)
    *p = 2000
    fmt.Println(*p)
}

6、練習題

6.1、練習1:寫一個程序,獲取一個變量的地址,並打印到終端

package main
import (
    "fmt"
)
func test1(){
    var a int = 5
    var p *int = &a
    fmt.Printf("a的地址是:%p\n",p)
    fmt.Printf("a的值是:%d",*p)
}
func main(){
    test1()
}
運行打印的結果如下:

6.2、練習2:寫一個函數,傳入一個int類型的指針,並在函數中修改所指向的值

 

func modify(x *int){         //傳入的變量x類型是指針 *int代表指針是整形的指針
    *x = 200                      //給x指針指向的值重新賦值為200
}
func test2(){
    var a int = 5             //定義一個整形的變量a,初始值是5
    var p *int = &a           //定義一個指針p,指向的是a
    fmt.Println("變量a的初始值是",a)   //打印a的初始值
    modify(p)                  //修改p指針指向的a的值
    fmt.Println("p指針指向的a變量的改變值是",a)   //打印a的值改變的結果

}
func main(){
    test2()
}
 
執行打印的結果如下:

 

二、內置函數

1、close:主要用來關閉channel

2、len:用來求長度,比如string、array、slice、map、channel

3、new:用來分配內存,主要用來分配值類型,比如int、struct。返回的是指針。示例可以看上面一的5.2,

4、make:用來分配內存,主要用來分配引用類型,比如channel、map、slice

5、append:用來追加元素到數組、slice中

6、panic和recover:用來做錯誤處理

7、new和make的區別

7.1、內建函數 new 用來分配內存,它的第一個參數是一個類型,不是一個值,它的返回值是一個指向新分配類型零值的指針

7.2、內建函數 make 用來為 slice,map 或 chan 類型分配內存和初始化一個對象(注意:只能用在這三種類型上),跟 new 類似,第一個參數也是一個類型而不是一個值,跟 new 不同的是,make 返回類型的引用而不是指針,而返回值也依賴於具體傳入的類型,具體說明如下:

   Slice: 第二個參數 size 指定了它的長度,它的容量和長度相同。
你可以傳入第三個參數來指定不同的容量值,但必須不能比長度值小。
比如 make([]int, 0, 10)

  Map: 根據 size 大小來初始化分配內存,不過分配后的 map 長度為 0,如果 size 被忽略了,那么會在初始化分配內存時分配一個小尺寸的內存

  Channel: 管道緩沖區依據緩沖區容量被初始化。如果容量為 0 或者忽略容量,管道是沒有緩沖區的

7.3、總結:

  new 的作用是初始化一個指向類型的指針(*T),make 的作用是為 slice,map 或 chan 初始化並返回引用(T)。

三、函數

1、聲明語法:func 函數名(參數列表)[(返回值列表)]{}

  func add(){

  }

  func add(a int,b int){

  }

  func add(a int, b int) int {

  }

  func add(a int, b int) (int , int){

  }

  func add(a , b int) (int , int){

  }

2、golang函數的特點:

  a、不支持重載,一個包不能有兩個名字一樣的函數;

  b、函數是一等公民,函數也是一種類型,函數也可以賦值給變量

  c、匿名函數

  d、多返回值

  示例: 

  type add_func func(int, int)int

  func oprator(op add_func,a int,b int)int {
      //使用傳進來的函數op,進行操作
      return op(a,b)
  }
  func add(a,b int) int {
      return a + b
  }  
  func main(){
      c:=add
      fmt.Println(c)
      sum := oprator(c,100,200)
      fmt.Println(sum)
 
  }
3、函數參數傳遞方式:
  3.1、值傳遞
  3.2、引用傳遞
  注意1:無論是值傳遞,還是引用傳遞,傳遞給函數的都是變量的副本。值傳遞是值的拷貝,一般來說地址拷貝更高效。值拷貝取決於拷貝的對象的大小,對象越大,性能越低。
  注意2:map、slice、channel、指針、interface默認以引用的方式傳遞
4、命名返回值的名字
//重命名一個返回值
func reduce(a , b int)(c int){
    c = a - b
    return
}
//重命名多個返回值
func calc(a,b int)(sum int,avg int){
    sum = a + b
    avg = sum/2
    return
}
// _ 忽略返回值
func main(){
    sum,_ := calc(100,200)
}
5、可變參數:
多個參數
func test11(arg...int) int{
}
一個或多個參數
func test11(a int ,arg...int) int{
}
二個或多個參數
func test11(a int,b int ,arg...int) int{
}

注意:其中arg是一個slice,我們可以通過arg[index]來獲取多個參數

通過len(arg)來判斷傳遞參數的個數

6、練習題示例

package main

import (
    "fmt"
)

//練習題一:寫一個函數add,支持1個或多個int相加,並返回結果
func add(a int,arg...int)int{
    var sum int
    //當只有一個參數時
    if len(arg)==0{
        sum = a
    }
    //當參數大於一個時
    if len(arg)>0{
        for i:=0;i<len(arg);i++{
            //注意這里但arg里面只有一個參數時,i是等於0即arg[0]
            if i == 0 {
                sum = a + arg[0]
            }
            //當arg參數大於一個時
            if i >0 {
                sum+=arg[i]
            }
        }
    }
    return sum
}

//練習題二:寫一個函數concat,支持1個或多個string相拼接,並返回結果
func concat(a string,arg...string)string{
    var str string
    if len(arg)==0{
        str = a
    }
    if len(arg)>0{
        for i:=0;i<len(arg);i++{
            if i == 0 {
                str = a + arg[0]
            }
            if i >0 {
                str+=arg[i]
            }
        }
    }
    return str
}


func main(){
    fmt.Println(add(-22,23,555))
    fmt.Println(concat("aaa","bbbbb","ccccccc"))
}
 
7、defer的特點和用途
7.1、當函數返回時,執行defer語句。因此可以用來做資源清理
7.2、多個defer語句,按先進后出的方式執行
示例:
func a(){
    fmt.Println("defer from a")
    i:=0
    defer fmt.Println(i)   //defer語句中的變量,在defer聲明時就決定
    i++ 
}
func f(){
    fmt.Println("defer from f")
    for i:=0;i<5;i++{
        defer fmt.Println(i)
    }   
}

func main(){
    //練習三
    a()
    f()
}
打印的結果:
7.3、defer語句中的變量,在defer聲明時就決定了
7.4、defer用途:關閉文件句柄,鎖資源釋放,數據庫連接釋放
 
四、遞歸函數
1、一個函數調用自己,就叫遞歸函數
2、斐波那契數
//斐波那契數列示例:
func fb( n int)int{
    if n <=1{
        return 1
    }
    return fb(n-1)+fb(n-2)
}
func main(){
    for i:=0;i<10;i++{
        n := fb(i)
        fmt.Println(n)
    }
}
3、遞歸的設計原則
3.1、一個大問題能夠分解成相似的小問題
3.2、定義好出口條件(即結束遞歸循環的條件)
 
五、閉包
一個函數和與其相關的引用環境的組合成的實體
//示例一:
func Adder() func(int)int{
    var x int
    return func(delta int)int{
        x +=delta
        return x
    }
}
//示例2:
func makeSuffixFunc(suffix string) func(string)string{
    return func(name string)string{
        if !strings.HasSuffix(name ,suffix){
            return name + suffix
        }
        return name
    }
}
func main(){
    //練習五:閉包
    //示例1:
    // var f = Adder()
    // fmt.Println(f(1))
    // fmt.Println(f(10))
    // fmt.Println(f(100))
    //示例2:
    func1 := makeSuffixFunc(".jpg")
    func2 := makeSuffixFunc(".txt")
    fmt.Println(func1(func1("test")))
    fmt.Println(func2("test"))
}
 
六、數組和切片
1.1、排序和查找操作
  排序操作主要都在sort包中,導入就可以使用
  sort.Ints對整數進行排序, sort.Strings對字符串串進⾏行行排序, sort.Float64s對 浮點數進⾏行行排序.
  sort.SearchInts(a []int, b int) 從數組a中查找b,前提是a 必須有序
   sort.SearchFloats(a []float64, b float64) 從數組a中查找b,前提是a 必須有序
  sort.SearchStrings(a []string, b string) 從數組a中查找b,前提是a 必須有序

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM