Go語言學習2-基本詞法

2. Go語言基本詞法

Go語言的語言符號又稱爲詞法元素,共包括5類:標識符(identifier)、關鍵字(keyword)、操做符(operator)、分隔符(delimiter)、以及字面量(literal)。通常狀況下,空格符、水平製表符、回車符和換行符都會被忽略,除非它們做爲多個語言符號之間的分隔符的一部分。在Go語言中不須要顯示地插入分號,在必要時,Go語言會自動爲代碼插入分號以進行語句分隔。編程

Go語言代碼由若干個Unicode字符組成,Go語言的全部源代碼都必須由Unicode編碼規範的UTF-8編碼格式進行編碼(也就是說編寫的Go語言源碼文件必須是UTF-8編碼格式的)。數組

2.1 標識符

Go語言的標識符是由若干字母(由Unicode編碼便可)、下劃線和數字組成的字符序列;該字符序列的第一個字符必須爲字母。併發

注意:app

  1. 在Go語言代碼中,每個標識符都必須在使用前進行聲明。
  2. 一個聲明將一個非空的標識符與一個常量、類型、變量、函數或代碼包綁定在一塊兒。
  3. 在同一個代碼塊中,不容許重複聲明同一個標識符(除了賦值語句例外)。
  4. 在一個源碼文件和一個代碼包中的標識符都須要遵循此規則。
  5. 一個已被聲明的標識符的做用域與其直接所屬的代碼塊的範圍相同。

嚴格來說,代碼包聲明語句並不算是一個聲明。由於代碼包名稱並不會出如今任何一個做用域中。代碼包聲明語句的目的是爲了鑑別若干源碼文件是否屬於同一個代碼包,或者指定導入代碼包時的默認代碼包引用名稱。框架

限定標識符用來訪問其餘代碼包中的變量或類型。例如,當我須要訪問代碼包os中名爲O_RDONLY的常量時,須要這樣寫os.O_RDONLY。編程語言

限定標識符可以使用,須要知足兩個前提條件:ide

  1. 要訪問的代碼包必須被事先導入;
  2. 這個代碼包中的標識符必須是可導出的。

一個可導出的標識符也須要知足兩個前提條件:函數

  1. 標識符名稱中的第一個字符必須爲大寫(Go語言根據標識符名稱中的第一個字符的大小寫來肯定這個標識符的訪問權限的,當標識符名稱的第一個字符爲大寫時,其訪問權限爲「公開的」,也就是該標識符能夠被任何代碼包中的任何代碼經過限定標識符訪問到;當標識符的第一個字符爲小寫時,其訪問權限就是"包級私有的",也就是隻有與該標識符同在一個代碼包的代碼纔可以訪問到它);
  2. 標識符必須是被聲明在一個代碼包中的變量或者類型的名稱,或者是屬於某個結構體類型的字段名稱或方法的名稱。

Go語言的預約義標識符:ui

  • 全部基本數據類型的名稱。
  • 接口類型error
  • 常量true,false和iota
  • 全部內建函數的名稱,即append、cap、close、complex、copy、delete、imag、len、make、new、panic、print、println、real和recover。

Go語言中有一個空標識符,它由一個下劃線表示,通常用於一個不須要引入一個新綁定的聲明中。例如,當咱們只想執行一下某個代碼包中的初始化函數,而不須要使用這個代碼包中的任何程序實體的時候,能夠編寫以下導入語句:編碼

import _ "runtime/cgo"

其中,"runtime/cgo"表明了一個標準庫代碼包的標識符。

2.2 關鍵字

關鍵字(也稱爲保留字)是被編程語言保留而不讓編程人員做爲標識符使用的字符序列。

類別 關鍵字
程序聲明 import, package
程序實體聲明和定義 chan, const, func, interface, map, struct, type, var
程序控制流程 go, select, break, case, continue, default, defer, else, fallthrough, for, goto, if, range, return, switch

在Go語言中,程序實體的聲明和定義是創建在其數據類型的體系之上的。例如關鍵字chan、func、interface、map和struct,分別於Go語言的複合數據類型Channel(通道)、Function(函數)、Interface(接口)、Map(字典)和Struct(結構體)相對應。

程序控制流程的關鍵字,一共15個。其中go和select,這兩個主要用於Go語言併發編程。

2.3 字面量

Go語言代碼中用到的字面量有如下3類:

  1. 表示基礎數據類型值的各類字面量。例如,表示浮點數類型值的12E-3。
  2. 構造各類自定義的複合數據類型的類型字面量。例如,下面表示一個名稱爲Person的自定義結構體類型:

    type Person struct {
        Name    string
        Age     uint8
        Address string
    }
  3. 表示複合數據類型的值的複合字面量,被用來構造類型Struct(結構體)、Array(數組)、Slice(切片)和Map(字典)的值。例如,下面的字面量用於表示上面名稱爲Person的結構體類型的值:

    Person {
        Name:"Huazie",
        Age: "21",
        Address: "Nanjing, China"
    }

注意:
對複合字面量的每次求值都會致使一個新的值被建立。所以,如上該複合字面量每被求值一次就會建立一個新的Person類型的值。

Go語言不容許在一個此類的複合字面變量中,出現重複的鍵。以下都是錯誤,沒法經過編譯,由於鍵都有重複。

//表示結構體類型值,有重複的鍵 Name
Person {Name: "Huazie",Age: "21", Name: "Unknown"}
//表示字典類型值,有重複的鍵 Age
map[string]string{ Name: "Huazie",Age: "21", Age: "21"}
//表示切片類型值,有重複的鍵 0
[]string{0: "0", 1: "1", 0: "-1"}

2.4 類型

一個類型肯定了一類值的集合,以及能夠在這些值上施加的操做。類型能夠由類型名稱或者類型字面量指定,分爲基本類型和複合類型,基本類型的名稱能夠表明其自身。

var bookName string

如上聲明瞭一個類型爲string(基本類型中的一個)、名稱爲bookName的變量。

其餘基本類型(預約義類型)有bool、byte、rune、int/uint、int8/uint八、int16/uint1六、int32/uint3二、int64/uint6四、float3二、float6四、complex64和complex128。除了bool和string以外的其餘基本類型也叫作數值類型。

複合類型通常由若干(也包括零)個其餘已被定義的類型組合而成。複合類型有Channel(通道)、Function(函數)、Interface(接口)、Map(字典)、Struct(結構體)、Slice(切片)、Array(數組)和Pointer(指針)。

Go語言中的類型又能夠分爲靜態類型和動態類型。一個變量的靜態類型是指在變量聲明中給出的那個類型。絕大多數類型的變量都只有靜態類型。惟獨接口類型的變量例外,它除了擁有靜態類型以外,還擁有動態類型(接口類型在後面會講到)。

每個類型都會有一個潛在類型。若是這個類型是一個預約義類型(也就是基本類型),或者是一個由類型字面量構造的複合類型,那麼它的潛在類型就是它自身。如string類型的潛在類型就是string類型,上面自定義的Person類型的潛在類型就是Person。若是一個類型並不屬於上述狀況,那麼這個類型的潛在類型就是類型聲明中的那個類型的潛在類型。

以下聲明一個自定義類型

type MyString string

如上能夠把類型MyString看做string類型的一個別名類型,那麼MyString類型的潛在類型就是string類型。Go語言基本數據類型中的rune類型能夠看做是uint32類型的一個別名類型,其潛在類型就是uint32。

注意:

  • 類型MyString和類型string是兩個不相同的類型。不能將其中一個類型的值賦給另外一個類型的變量。
  • 別名類型與它的源類型的不一樣僅僅體如今名稱上,它們的內部結構是一致的;下面的類型轉換的表達式都是合法的:MyString("ABC") 和string(MyString("ABC"))。這種類型轉換並不會建立新的值。

一個類型的潛在類型具備可傳遞性,以下:

type iString MyString

則類型isString的潛在類型就是string類型。

這裏聲明一個類型,以下:

type MyStrings [3]string

注意:類型MyStrings的潛在類型並非[3]string。[3]string既不是一個預約義的類型,也不是一個由類型字面量構造的複合類型,而是一個元素類型爲string的數組類型。

根據上面的定義可知類型MyStrings的潛在類型就是[3]string的潛在類型string。

Go語言規定,一個數組類型的潛在類型決定了在該類型的變量中能夠存放哪個類型的元素。

2.5 操做符

操做符就是用於執行特定算術運算或邏輯操做的符號。(這裏不詳細講解了,跟C語言的操做符相似),不過Go語言中沒有三元操做符,因此除了一元操做符之外都一定是二元操做符。Go語言一共有21個操做符,包括算術操做符、比較操做符、邏輯操做符、地址操做符和接收操做符。

符號 說明 示例
邏輯或操做。二元,邏輯操做符 true false //表達式結果是true
&& 邏輯與操做。二元,邏輯操做符 true && false //表達式結果是false
== 相等判斷操做。二元,比較操做符 "abc" == "abc"//結果是true
!= 不等判斷操做。二元,比較操做符 "abc" != "Abc"//結果是true
< 小於判斷操做。二元,比較操做符 1 < 2 //表達式結果是true
<= 小於或等於。二元,比較操做符 1 <= 2 //表達式結果是true
\> 大於判斷操做。二元,比較操做符 3 > 2 //表達式結果是true
\>= 大於或等於。二元,比較操做符 3 >= 2 //表達式結果是true
+ 表示求和,一元又是二元,算術操做符 +1 //結果爲1 (1+2) //結果是3
- 表示求差,一元又是二元,算術操做符 -1 //結果爲-1 (1 – 2) //結果是-1
\ 按位或操做,二元,算術操做符 5 \ 11 //表達式的結果是15
^ 按位異或,一元又是二元,算術操做符 5^11//結果是14(^5)//結果是-6
* 求積或取值,一元,二元,算術,地址 *p //取值操做
/ 求商操做,二元,算術操做符 10 / 5 //表達式的結果爲2
% 求餘數操做,二元,算術操做符 12 % 5 //表達式的結果爲2
<< 按位左移操做,二元,算術操做符 4 << 2 //表達式的結果爲16
\>\> 按位右移操做,二元,算術操做符 4 >> 2 //表達式的結果爲1
& 按位與操做,一元,二元,算術,地址 &v //取地址操做
&^ 按位清除操做,二元,算術操做符 5 &^ 11 //表達式的結果爲4
! 邏輯非操做,一元,邏輯操做符 !b //若b爲true,結果爲false
<- 接收操做,一元,接收操做符 <- ch

注意:假設上面的ch 表明了元素類型爲 byte的通道類型值,則<- ch表示從ch中接收byte類型值的操做。

重點講解3個操做符

  1. &^ 實現了按位清除操做,按位清除就是根據第二個操做數的二進制值對第一個操做數的二進制值進行相應的清零操做,若是第二個操做數的某個二進制位上的數組爲1,就把第一個操做數的對應二進制位上的數值設置爲0。不然,第一個操做數的對應二進制位上的數值不變。這樣的操做並不會改變第一個操做數的原值,只會根據兩個操做數的二進制值計算出結果值。這樣就能夠理解上面的5 &^ 11的結果爲4了。
  2. ^ 做爲一元操做符,分兩種狀況:
    (1). 操做數是無符號的整數類型,使用這一個操做就至關於對這個操做數和其整數類型的最大值進行二元的按位異或操做,以下:

    ^uint8(1)           = 254     //無符號整數的一元按位異或操做
    00000001 ^ 11111111 = 11111110//對應的二進制數運算

    如上,內置函數uint8會將一個整數字面量轉換爲一個uint8類型的值,這保證了一元操做符^的惟一操做數必定是一個無符號整數類型的值。
    (2). 操做是有符號的整數類型,這一操做就至關於對這個操做數和-1進行二元按位異或操做。例如:

    ^1                  = -2 //有符號整數的一元按位異或操做
    00000001 ^ 11111111 = 11111110//對應的二進制運算

    注意:以上的操做數的二進制值都是以補碼形式表示;默認狀況下整數字面量是有符號的,因此(2)中操做數1不須要顯示使用內置函數int8 。

  3. <- 接收操做符,只做用於通道類型的值。使用時,須要注意兩點:
    (1). 從一個通道類型的空值(即nil)接收值的表達式將會永遠被阻塞。
    (2). 從一個已被關閉的通道類型值接收值會永遠成功並當即返回一個其元素類型的零值。

一個由接收操做符和通道類型的操做數所組成的表達式能夠直接被用於變量賦值或初始化,以下所示(在賦值語句講解時,再細說)

v1 := <-ch
v2 = <-ch

特殊標記 = 用於將一個值賦給一個已被聲明的變量或常量。
特殊標記 := 則用於在聲明一個變量的同時對這個變量進行賦值,且只能在函數體內使用。

又以下:

v, ok = <-ch
v, ok := <-ch

當同時對兩個變量進行賦值或初始化時,第二個變量將會是一個布爾類型的值。這個值表明了接收操做的成功與否。若是這個值爲false,就說明這個通道已經被關閉了。(以後講解通道類型會詳細介紹)。

操做符優先級

優先級 操做符
5 *     /    %    <<    >>    &    &^
4 +     -     \     ^
3 ==     !=     <     <=    >     >=
2 &&
1

2.6 表達式

基本表達式

(1) 使用操做數來表示;

(2) 使用類型轉換來表示;

(3) 使用內建函數調用來表示;

(4) 一個基本表達式和一個選擇符號組成選擇表達式;

例如,若是在一個結構體類型中存在字段f,咱們就能夠在這個結構體類型的變量x上應用一個選擇符號來訪問這個字段f,即x.f。其中,.f就是一個選擇符號。注意:前提是這個變量x的值不能是nil。在Go語言中,nil用來表示空值。

(5) 一個基本表達式和一個索引符號組成索引表達式;

索引符號由狹義的表達式(僅由操做符和操做數組成)和外層的方括號組成,例如[]int{1,2,3,4,5}[2]就是索引表達式。
Go語言容許以下的賦值語句:

v, ok := a[x]

如上a爲字典類型,x爲字典的鍵。該索引表達式的結果是一對值,而不是單一值。第一個值的類型就是該字典類型的元素類型,而第二個值則是布爾類型。與變量ok綁定的布爾值表明了在字典類型a中是否包含了以x爲鍵的鍵值對。若是在a中包含這樣的鍵值對,那麼賦給變量ok的值就是true,不然就爲false。

注意:雖然當字典類型的變量a的值爲nil時,求值表達式a[x]並不會發生任何錯誤,可是在這種狀況下對a[x]進行賦值卻會引發一個運行時恐慌( Go語言異常)。

(6) 一個基本表達式和一個切片符號組成切片表達式;

切片符號由2個或3個狹義的表達式和外層的方括號組成,這些表達式之間由冒號分隔。切片符號做用與索引符號相似,只不過索引符號針對的是一個點,切片符號針對的是一個範圍。例如,要取出一個切片[]int{1,2,3,4,5}的第二個到第四個元素,那麼可使用切片符號的表達式[]int{1,2,3,4,5}[1:4],該結果仍是一個切片。

切片表達式a[x:y:z],a是切片符號[x:y]的操做對象。其中,x表明了切片元素下界索引,y表明了切片的元素上界索引,而z則表明了切片的容量上界索引。約束以下:

0 <= 元素下界索引 <= 元素上界索引 <= 容量上界索引 <= 操做對象的容量

設a的值爲[]int{1,2,3,4,5},則切片表達式a[:3]等同於a[0:3],這是由於切片符號的元素下界索引的默認值爲0,相應的元素上界的索引的默認值爲操做對象的長度值或容量值,即切片表達式a[3:]等同於a[3:5]。一樣,切片表達式a[:]等同於複製a所表明的值並將這個複製品做爲表達式的求值結果。

注意: UTF-8 編碼格式會以3個字節來表示一箇中文字符,而切片操做是針對字節進行的。

若是有「Go併發編程實戰」的字符串類型的變量a,那麼切片表達式a[1:3]的結果不是「o並」,而a[1:5]的結果纔是「o並」。

(7) 一個基本表達式和一個類型斷言符號組成;

類型斷言符號以一個英文句號爲前綴,並後跟一個被圓括號括起來的類型名稱或類型字面量。類型斷言符號用於判斷一個變量或常量是否爲一個預期的類型,並根據判斷結果採起不一樣的響應。例如,若是要判斷一個int8類型的變量num是不是int類型,能夠這樣編寫表達式:interface{}(num).(int)。

對於一個求值結果爲接口類型值的表達式x和一個類型T,對應的類型斷言爲:

x.(T)

該表達式的做用是判斷「x不爲nil且存儲在其中的值是T類型的」是否成立。

若是T不是一個接口類型,那麼x.(T)會判斷類型T是否爲x的動態類型(一個變量的動態類型就是在運行期間存儲在其中的值的實際類型);而這個實際類型必須是該變量聲明的那個類型的一個實現類型,不然就根本不可能在該變量中存儲這一類型的值。因此類型T必須爲x的類型的一個實現類型,而在Go語言中只有接口類型能夠被其餘類型實現,因此x的求值結果必須是一個接口類型的值。

因此上面表達式interface{}(num).(int)中表達式interface{}(num)的含義就是將變量num轉換爲interface{}類型的值(即它的結果值是接口類型的),而這恰好符合前面的定義。

知識點: interface{}是一個特殊的接口類型,表明空接口。全部類型都是它的實現類型。

在對變量的賦值或初始化的時候,也可使用類型斷言,以下:

v, ok := x.(T)

當使用類型斷言表達式同時對兩個變量進行賦值時,若是類型斷言成功,那麼賦給第一個變量的將會是已經被轉換爲T類型的表達式x的求值結果,不然賦給第一個變量的就是類型T的零值。布爾類型會被賦給變量ok,它體現了類型斷言的成功(true)與否(false)。

注意: 在這種場景下,即便類型斷言失敗也不會引起運行時恐慌。

(8) 一個基本表達式和一個調用符號組成。

調用符號只針對於函數或者方法。與調用符號組合的基本表達式不是一個表明代碼包名稱(或者其別名)的標識符就是一個表明結構體類型的方法的名稱的標識符。調用符號由一個英文句號爲前綴和一個被圓括號括起來的參數列表組成,多個參數列表之間用逗號分隔。例如,基本表達式os.Open(「/etc/profile」)表示對代碼包os中的函數Open的調用。

可變長參數

若是函數f能夠接受的參數的數量是不固定的,那麼函數f就是一個可以接受可變長參數的函數,簡稱可變參函數。在Go語言中,在可變參函數的參數列表的最後總會出現一個可變長參數,這個可變長參數的類型聲明形如…T。Go語言會在每次調用函數f的時候建立一個切片類型值,並用它來存放這些實際函數。這個切片類型值的長度就是當前調用表達式中與可變長參數綁定的實際參數的數量。

可變參函數appendIfAbsent聲明以下(函數體省略):

func appendIfAbsent(s []string, t ...string) []string

針對此函數的調用表達式以下:

appendIfAbsent([]string(「A」,」B」,」C」),」C」,」B」,」A」)

其中,與可變參數t綁定的切片類型值爲[]string{」C」,」B」,」A」},包含了實際參數」C」,」B」和」A」。

也能夠直接把一個元素類型爲T的切片類型值賦給…T類型的可變長參數,以下調用:

appendIfAbsent([]string(「A」,」B」,」C」), []string(」C」,」B」,」A」)...)

或者若是有一個元素類型爲stirng的切片類型的變量s的話,以下調用:

appendIfAbsent([]string(「A」,」B」,」C」), s...)

對於將切片類型的變量賦給可變長參數的狀況,Go語言不會專門建立一個切片類型值來存儲其中的實際參數。由於,這樣的切片類型值已經存在了,可變長參數t的值就是變量s的值。

關於Go語言基本詞法的講解就告一段落了,接下來要講Go語言的數據類型了。

最後附上知名的Go語言開源框架(每篇附上一個):

Beego: 一個國產的HTTP框架。咱們能夠用它來快速地開發各類應用程序。官網:http://beego.me

相關文章
相關標籤/搜索