1. 程式人生 > >golang-指針,函數,map

golang-指針,函數,map

問題 多個參數 宕機 打印 自動 %d signal map() tor

指針

普通類型變量存的就是值,也叫值類型。指針類型存的是地址,即指針的值是一個變量的地址。
一個指針只是值所保存的位置,不是所有的值都有地址,但是所有的變量都有。使用指針可以在無需知道
變量名字的情況下,間接讀取或更新變量的值。

獲取變量的地址,用&,例如:var a int 獲取a的地址:&a,&a(a的地址)這個表達式獲取一個指向整型變量的指針,它的類型是整形指針(*int),如果值叫做p,我們說p指向x,或者p包含x的地址,p指向的變量寫成
*p ,而*p獲取變量的值,這個時候*p就是一個變量,所以可以出現在賦值操作符的左邊,用於更新變量的值

指針類型的零值是nil
兩個指針當且僅當指向同一個變量或者兩者都是nil的情況才相等

通過下面小例子進行理解指針:

package main

import "fmt"

func main() {
    x := 1
    //&x 獲取的是變量x的地址,並賦值給p,這個時候p就是一個指針
    //p是指針,所以*p獲取的就是變量的值,指針指向的是變量x的值,即*p為1
    p := &x
    fmt.Println(*p)
    //這裏*p 進行賦值,也就是更改了變量x的值,即實現不知道變量的名字更改變量的值
    *p = 2
    fmt.Println(x)
}

再看一個關於通過一個函數來修改變量值的問題:

package main

import 
"fmt" func modify(num int) { num = 1000 } func main() { a := 1 modify(a) fmt.Println(a) }

這個例子是修改變量的值,但是最後打印變量a的值是還是10,所以這裏就需要知道,當通過定義的函數modify來修改變量的值時,傳入變量a其實會進行一次拷貝,傳入的其實是a變量的一個副本,所以當通過
modify修改的時候修改的是副本的值,並沒有修改變量a的值。
當我們理解指針的之後,就可以通過指針的的方法來解決上面的這個問題,將代碼更改為:

 
package main

import "fmt
" func modify(num *int) { *num = 1000 } func main() { a := 1 modify(&a) fmt.Println(a) }

內置函數

len: 用於求長度,比如string、array、slice、map、channel
new: 來分配內存,主要來分配值類型,如int、struct。返回的是指針
make: 來分配內存,主要 來分配引 類型, 如chan、map、slice
append: 來追加元素到數組、slice中
panic和recover: 來做錯誤(這個後續整理)

下面重點整理new和make

new函數

func new(Type) *Type
先看一下官網對這個內置函數的介紹:
內置函數 new 用來分配內存,它的第一個參數是一個類型,不是一個值,它的返回值是一個指向新分配類型零值的指針。這裏要特別註意new返回的是一個指針

new函數也是創建變量的一種方式。表達式new(T)創建一個未命名的T類型變量,初始化T類型的零值,並返回其地址(地址類型為*T)

通過下面例子進行理解:

package main

import "fmt"

func newFunc() {
    p := new(int)
    fmt.Println(p)  //打印是地址
    fmt.Println(*p) //int類型的零值為0這裏打印0
    *p = 2
    fmt.Println(*p) //*p已經為其地址指向了一個變量2,所以這裏打印為2
}

func main() {
    newFunc()
}

這裏我們要知道new創建的變量和取其地址的普通局部變量沒有什麽不同,只是語法上的便利

如果我們定義一個指針是不能直接給這個指針賦值的,而是需要先給這個指針分配內存,然後才能賦值
下面例子先不初始化分配內存,直接賦值會報錯,如下:

package main

import "fmt"

//new創建變量的方式
func newIntFunc() *int {
    return new(int)
}

//取地址的普通局部變量
func newIntFunc2() *int {
    var res int
    return &res
}

func main() {
    var p1 *int
    /**
        註意下面這句p1 = newIntFunc()必須有,否則會報錯
        <nil>
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x108d723]

    goroutine 1 [running]:
    main.main()
            /Users/zhangjinyu/learngo/src/go_dev/day04/new1/new1.go:18 +0x63
    exit status 2
    */
    p1 = newIntFunc()
    fmt.Println(p1)
    *p1 = 22
    fmt.Println(*p1)

    var p2 *int
    p2 = newIntFunc2()
    fmt.Println(p2)
    *p2 = 33
    fmt.Println(*p2)
}

make函數

func make(Type, size IntegerType) Type
先看一下官網對這個內置函數的介紹:
內置函數make用來為slice,map或chan類型分配內存或初始化一個對象(這裏需要註意:只能是這三種類型)
第一個參數也是一個類型而不是一個值
返回的是類型的引用而不是指針,而且返回值也依賴具體傳入的類型
註意:make返回初始化後的(非零)值。

其實在上一篇整理切片slice的時候就用到了make如:
make([]type,len)
當時通過make來初始化slice的時候,第二個參數指定了它的長度,如果沒有第三個參數,它的容量和長度相等,當然也可以傳入第三個參數來指定不同的容量值,但是註意不能比長度值小

這裏提前說一下通過make初始化map的時候,根據size大小來初始化分配內存,不過分配後的map長度為0,如果size被忽略了,會在初始化分配內存的時候分配一個小的內存

關於new和make的一個小結:
new 的作用是初始化一個指向類型的指針 (*T),make的作用是為slice,map或者channel初始化,並且返回引用 T

函數

函數的聲明語法:func 函數名 (參數 表) [(返回值 表)] {}
這了要註意第一個花括號必須和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){

}

golang函數的特點:

    1. 不支持重載,即一個包不能有兩個名字一樣的函數
    2. 函數也是一種類型,一個函數可以賦值給變量
    3. 匿名函數
    4. 多返回值

演示一些函數的例子:

package main

import (
    "fmt"
)

func add(a, b int) int {
    return a + b
}

func main() {
    c := add //這裏把函數名賦值給變量c
    fmt.Printf("%p %T", c, add)
    sum := c(10, 20) //調用c其實就是在調用add
    fmt.Println(sum)

}

還有一個:

package main

import (
    "fmt"
)

type addFunc func(int, int) int

func add(a, b int) int {
    return a + b
}

func operator(op addFunc, a int, b int) int {
    return op(a, b)
}

func main() {
    c := add
    sum := operator(c, 100, 200)
    fmt.Println(sum)
}

變量作用域

在函數外面的變量是全局變量
函數內部的變量是局部變量
go中變量的作用域有多種情況:
函數級別的,代碼塊級別的

關於函數的可變參數

變長函數被調用的時候可以有可變的參數個數
在參數列表最後的類型名稱前使用省略號...可以聲明一個變長的函數,
例如:
0個或多個參數
func add(arg...int) int{

}
1個或多個參數
func add(a int,arg...int) int{

}
2個或多個參數
func add(a int,b int,arg...int)int{

}

關於函數參數的傳遞

不管是值類型還是引用傳遞,傳遞給函數的都是變量的副本
註意:map,slice,chan,指針,interface默認以引用方式傳遞

延遲函數defer的調用

語法上,一個defer語句就是一個普通的函數或者方法調用,在調用之前加上關鍵字defer。函數和參數表達式會在語句執行時求值,但是無論是正常情況還是執行return語句或者函數執行完畢,以及不正常情況下,如程序發生宕機,實際的調用推遲到包含defer語句的函數結束後才執行,defer語句沒有限制使用次數。

defer用途:

  1. 當函數返回時,執行defer語句,因此可以用來做資源清理
  2. 多個defer語句,按先進後出的方式執行
  3. defer語句中的變量,在defer聲明時就決定了

先通過一個小例子理解defer:

package main

import (
    "fmt"
)


func testDefer(){
    a := 100
    fmt.Printf("before defer:a=%d\n",a)
    defer fmt.Println(a)
    a = 200
    fmt.Printf("after defer:a=%d\n",a)

}

func main(){
    testDefer()
}

這裏我們可以這樣理解當我們執行defer語句的時a=100,這個時候壓入到棧中,等程序最後結束的時候才會調用defer語句,所以打印的順序是最後才打印一個數字100

defer語句經常使用成對的操作,比如打開和關閉,連接和斷開,加鎖和解鎖

下面拿關閉一個打開文件操作為例子,當我們通過os.Open()打開一個文件的時候可以在後面添加defer f.Close() 這樣在函數結束時就可以幫我們自動關閉一個打開的文件

map類型

key-value的數據結構,又叫字典

聲明
var map1 map[keytype]valuetype

例子:
var a map[string]string
var a map[string]int

註意:聲明是不會分配內存的需要make初始化

初始化的兩種方式:
var map[string]string = map[string][string]{"hello","world"}
或:
var a = make(map[string]string,10)

插入和更新
a["hello"] = "world"

查找
val,ok := a["hello"]

遍歷
for k,v := range a{
fmt.println(k,v)
}

刪除
delete(a,"hello")

這個操作是安全的,及時這個元素不存在也不會報錯,如果一個查找失敗將返回value類型對應的零值

長度
len(a)

map是引用類型

註意:map中的元素並不是一個變量,所以我們不能對map的元素進行取址操作

如下:

package main

import "fmt"

func createMap() {
    /*創建map的四種方式
        1) make(map[KeyType]ValueType, initialCapacity)
    2) make(map[KeyType]ValueType)
    3) map[KeyType]ValueType{}
    4) map[KeyType]ValueType{key1 : value1, key2 : value2, ... , keyN : valueN}
    */
    map1 := make(map[string]string, 5)
    map2 := make(map[string]string)
    map3 := map[string]string{}
    map4 := map[string]string{"a": "1", "b": "2", "c": "3", "d": "4"}
    fmt.Println(map1, map2, map3, map4)
}

//填充和遍歷
func traversalMap() {
    map1 := make(map[string]string)
    map1["a"] = "1"
    map1["b"] = "2"
    map1["c"] = "3"
    for index, val := range map1 {
        fmt.Printf("%s->%s\n", index, val)
    }
}

//更新,查找,刪除
func updateMap() {
    fmt.Println("-----------------------------")
    map4 := map[string]string{"a": "1", "b": "2", "c": "3", "d": "4"}
    //查找,返回key對應的value,和是否存在的布爾值
    value, exist := map4["a"]
    fmt.Printf("%v->%v\n", exist, value)
    fmt.Println("=============================")
    //更新
    map4["a"] = "8"
    fmt.Printf("%v\n", map4)
    //刪除
    delete(map4, "b")
    fmt.Printf("%v\n", map4)

}

func main() {

    createMap()
    traversalMap()
    updateMap()
} 

轉自https://www.cnblogs.com/zhaof/p/8129424.html

golang-指針,函數,map