golang 類型-----沒有markdown真是鬧騰@51cto

1. 變量
2. 命名
3. 常量
4. 基本類型
5. 引用類型
6. 類型轉換
7. 自定義類型

變量

Go語言有兩種方式定義變量:

var 關鍵字
:= 短變量聲明符
  • var關鍵字

var x int   //自動初始化爲0
var y = false   //自動推斷爲bool類型

和C語言不同,類型被放在變量名之後,並且在運行時,爲了避免出現不可預測行爲,內存分配器會初始化變量爲二進制零值。 如果顯示初始化變量的值,可以省略變量類型,由編譯器推斷。

Go語言一次可以定義多個變量,並可以對其初始化成不同的類型,比如

var x,y int             //相同類型多個變量
var a, s = 10010, "hello go"    //不同類型多個變量

按照Go語言的編碼規範,建議以組的方式整理多行變量定義,比如:

var (
    x,y int
    a,s = 10010, "hello go"
    )
  • 短變量聲明符

在介紹詞法的章節中已經看到過這種用法,比如:

    x := 10010
    a , s := 10010, "hello go"

這種方式很簡單,但日常開發中新手經常犯下的一個錯誤,比如:

var x = 10010
func main(){    
    println(&x, x)
    ...
    x := "hello go" //對全局變量重新定義並初始化
    println(&x, x)
}

可想而知最終釀成的後果;爲了正確使用這種簡單的短變量聲明方式,請遵循如下限制:

  1. 定義變量的同時,顯示初始化
  2. 不能提供數據類型
  3. 只能在函數體內部使用
  4. 函數體內不能使用短變量重複聲明
  5. 不能使用短變量聲明這種方式來設置字段值
  6. 不能使用nil初始化一個未指定類型的變量

思考下面的代碼片段,輸出結果是什麼?


func test(){
    x := 1
    fmt.Println(x)
    {   
        fmt.Println(x)
        x = 3 
        x := 2
        fmt.Println(x)
        x = 5
    }
    fmt.Println(x)
}

如何檢查你的代碼中是否存在這樣的聲明?

# go tool vet -shadow yourfile.go

Go編譯器會將未使用的局部變量當作錯誤,比如:

func main() {
    x := 1
    var a int       //a declared and not used
    fmt.Println(x)
}

那麼思考一下下面的代碼,會不會報錯?

func main() {
    x := 1
    const a = 10010   // ???
    fmt.Println(x)
}

所以要記住函數體中存在未使用的常量,不會引發編譯器報錯;

常量

常量表示運行時恆定不變的值,由const關鍵字聲明,常量值必須是在編譯期可確定的字符、字符串、數字或布爾值。 聲明常量的同時可以指定其常量類型,或者由Go編譯器通過初始化值推斷,如果顯示指定其常量類型,那麼就必須保證常量左右值類型一致。 必要時要做類型轉換。並且右值不能超出常量類型的取值範圍,否則會造成溢出錯誤。比如:

const(
    x , y = 100, -10010
    b byte = byte(x)    //類型不一致需要做類型轉換
    c uint8 = uint8(y) //溢出
    )

思考:修改一下上面的代碼,只進行聲明而沒有對其初始化和指定類型,結果會怎樣?

const(
    x = 100
    y
    s string = "hello go"                                                                                                                                                                                      
    c
)
fmt.Printf("%T , %v\n",y,y)
fmt.Printf("%T , %v\n",c,c)

結論證明: 在常量組中如果不指定常量的類型和初始化值,則與上一行非空常量的右值相同

常量值可以是Go編譯器能計算出結果的表達式,如unsafe.Sizeof, len,cap,iota等。

枚舉

Go語言沒有明確上的定義枚舉enum類型,不過可以通過iota關鍵字來實現一組遞增常量的定義,比如:

const (
    _ = iota
    KB = 1 << (10 * iota)   // 1<<(10 * 1)
    MB                      // 1<<(10 * 2)
    GB                      // 1<<(10 * 3)
    )

多常量定義中也可以使用iota,它們各自單獨計算,確保常量組中每行常量的列數量相等即可,比如:

const(
    a ,b = iota,iota *10    //0 , 0 * 10
    c ,d                    //1 , 1 * 10
    )

如果中斷iota自增,需要顯示恢復,且後續的常量值將按行序遞增,比如:

const(
    a = iota
    b           //b = ???
    c = 100
    d           //d = ???
    e = iota    //c = ???
    f           //f = ???)

自增常量可以指定常量類型,甚至可以自定義類型,比如:

const(
    f float = iota //指定常量類型)type color byteconst(
    black color = iota  //自定義類型
    red
    blue
)
  • 常量VS變量

常量是“只讀”屬性
變量在運行期分配內存,而常量是在編譯期間直接展開,作爲指令數據使用
數字常量不會分配存儲空間,不能像變量那樣可以進行內存尋址

比如:

const x = 10 const y byte = x    // 相當於const y byte = 100const a int = 10    //顯示指定常量類型,編譯器做強類型檢查const b byte = a  // cannot use a (type int) as type byte in const initializer

基本類型

wKioL1h-2srBJJ6oAAEgBgrOzv8521.png


引用類型

特指slice,map和channel這三種預定義類型,他們具有比數字、數組等更復雜的存儲結構; 引用類型的創建必須是用make函數來創建,因爲除了分配內存之外,還有一系列屬性需要初始化,比如指針, 長度,甚至包括哈希分佈,數據隊列等。 而內置函數new只是按照指定類型長度分配零值內存,返回指針。比如:

p := new(map[string]int) //new函數返回指針
m := *p
m["go"] = 1     //panic: assignment to entry in nil map(運行時)

自定義類型

使用關鍵字type可以定義用戶自定義類型,包括基於基本數據類型創建,或者結構體和函數等類型,比如:

type flags byteconst (
    exec flags = 1 << iota
    write 
    read
)

與var、const類似,多個type定義也可以合併成組,比如:

type (
        user struct{
            name string
            age int
        }
        f func(user)
    )                       //自定義一組類型
    u := user{"Tom",20}    var say f = func(s user){
        fmt.Println(s.name, s.age)
    }
    say(u)

自定義類型與基礎類型相比,它們具有相同的數據結構,但它們不存在任何關係,除操作符外,自定義類型不會繼承基礎類型的其它信息,它們屬於完全不同的兩種類型, 即它們之間不能做隱式轉換,不能視爲別名甚至不能用於比較表達式。比如:

    type(
        data int
    )    var d data = 10
    var x int = d       //cannot use d (type data) as type int in assignment
    println(x == d) //invalid operation: x == d (mismatched types int and data)

未命名類型

與有明確標誌符的bool, int, string等類型相比,數組、切片、字典、通道等類型與具體元素類型或長度等屬性有關,所以稱作 未命名類型(unnamed type)。當然可以用type爲其提供具體名稱,將其改變爲命名類型(named type)。

具有相同聲明的未命名類型視作同一類型,比如:

1. 具有相同基類型的指針
2. 具有相同元素類型和長度的數組(array)
3. 具有相同元素類型的切片(slice)
4. 具有相同鍵值類型的字典(map)
5. 具有相同數據類型及操作方向的通道(channel)
6. 具有相同字段序列(字段名、字段類型、標籤以及字段順序)的結構體(struct)
7. 具有相同簽名(參數類型,參數順序和返回值列表,不包括參數名)的函數(func)
8. 具有相同方法集(方法名、方法簽名、不包括順序)的接口(interface)

結構體的tag經常被初忽視,它也屬於結構體類型組成的一部分,而不僅僅是元數據描述。

類型轉換

類型轉換在日常開發中是不可避免的問題,Go語言屬於強類型語言,除常量、別名以及未命名類型之外,其它不同類型之間的轉換都需要顯示類型轉換;好處是我們至少能知道語句及表達式的確切含義,比如:

a := 10         //int
b := byte(a)    //byte
c := a + int(b) //確保類型一致性
d := b + byte(a)//確保類型一致性

如果類型轉換的目標是指針、單向通道、沒有返回值的函數類型,那麼爲了避免語法歧義帶來的類型轉換錯誤,必須使用括號,比如:


x := 100
p := *int(&x) //cannot convert &x (type *int) to type int
                //invalid indirect of int(&x) (type int)
pc := <-chan int(c) //invalid operation: pc <- 3 (send to non-chan type int)

正確做法如下:

p := (*int)(&x) //(*int)(&x)
(<-chan int)(c) //單向通道
(func()) (f)     //無返回值的函數
(func()int) (f) //有返回值的函數,但是也可以不用括號:func()int(f),但是爲了影響程序可讀性,建議加上括號
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章