首頁 > 後端開發 > Golang > 主體

Go語言的詞法元素有幾類

青灯夜游
發布: 2023-01-12 10:03:16
原創
1775 人瀏覽過

Go語言的詞法元素有5類:1、標識符,由若干字母(由Unicode編碼即可)、底線和數字組成的字元序列;2、關鍵字,是被程式語言保留而不讓程式設計人員作為標識符使用的字元序列,也可以稱為保留字;3、操作符,是用於執行特定算術運算或邏輯操作的符號;4、分隔符號;5、字面量,是值的一種標記法。

Go語言的詞法元素有幾類

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

Go語言的語言符號又稱為詞法元素,共包含5類:識別碼(identifier)、關鍵字(keyword)、運算子(operator)、分隔符號(delimiter)、以及字面量(literal),它們是組成Go語言代碼和程式的最基本單位。

一般情況下,空格符、水平製表符、回車符和換行符都會被忽略,除非它們作為多個語言符號之間的分隔符號的一部分。在Go語言中不需要顯示地插入分號,在必要時,Go語言會自動為程式碼插入分號以進行語句分隔。

Go語言代碼由若干個Unicode字元組成,Go語言的所有原始程式碼都必須由Unicode編碼規範的UTF-8編碼格式進行編碼(也就是說編寫的Go語言原始碼檔案必須是UTF- 8編碼格式的)。

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

Go語言代碼由若干個Unicode字元組成,Go語言的所有原始程式碼都必須由Unicode編碼規範的UTF-8編碼格式進行編碼(也就是說編寫的Go語言原始碼檔案必須是UTF- 8編碼格式的)。

1、識別碼

Go語言的識別碼是由若干字母(由Unicode編碼即可)、底線和數字組成的字元序列;此字元序列的第一個字元必須為字母。

注意:

  • #在Go語言程式碼中,每個識別碼都必須在使用前進行宣告。

  • 一個宣告將一個非空的標識符與一個常數、型別、變數、函數或程式碼包綁定在一起。

  • 在同一個程式碼區塊中,不允許重複宣告同一個識別碼(除了賦值語句例外)。

  • 在一個原始碼檔案和一個程式碼包中的識別碼都需要遵循此規則。

  • 一個已被宣告的識別碼的作用域與其直接所屬的程式碼區塊的範圍相同。

嚴格來講,程式碼包宣告語句並不算是一個宣告。因為代碼包名稱並不會出現在任何一個作用域中。程式碼包宣告語句的目的是為了辨別若干原始碼檔案是否屬於同一個程式碼包,或是指定匯入程式碼包時的預設程式碼包引用名稱。

限定標識符用來存取其他程式碼包中的變數或型別。例如,當我需要存取代碼包os中名為O_RDONLY的常數時,需要這樣寫os.O_RDONLY。

限定識別碼能夠使用,需要滿足兩個前提條件:

  • 要存取的程式碼包必須事先匯入;

  • 這個程式碼包中的識別碼必須是可匯出的。

一個可匯出的識別碼也需要滿足兩個前提條件:

  • #標識符名稱中的第一個字元必須為大寫(Go語言根據標識符名稱中的第一個字元的大小寫來確定這個標識符的存取權限的,當標識符名稱的第一個字元為大寫時,其存取權限為“公開的”,也就是此標識符可以被任何代碼包中的任何代碼透過限定標識符存取到;當標識符的第一個字元為小寫時,其存取權限就是"包級私有的",也就是只有與該標識符同在一個程式碼包的程式碼才能夠存取到它);

  • 標識符必須是被宣告在一個程式碼包中的變數或類型的名稱,或是屬於某個結構體類型的欄位名稱或方法的名稱。

Go語言的預先定義標識符:

  • 所有基本資料類型的名稱。
  • 介面類型error
  • 常數true,false和iota
  • #所有內建函數的名稱,即append、cap、close、complex、copy、delete、imag、len 、make、new、panic、print、println、real和recover。

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

#
import _ "runtime/cgo"1.
登入後複製

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

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语言并发编程。

3、字面量

简单来说,字面量就是值的一种标记法。但是,在Go中,字面量的含义要更加广泛一些。

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"}
登入後複製

4、类型

一个类型确定了一类值的集合,以及可以在这些值上施加的操作。类型可以由类型名称或者类型字面量指定,分为基本类型和复合类型,基本类型的名称可以代表其自身。

var bookName string1.
登入後複製

如上声明了一个类型为string(基本类型中的一个)、名称为bookName的变量。

其他基本类型(预定义类型)有bool、byte、rune、int/uint、int8/uint8、int16/uint16、int32/uint32、int64/uint64、float32、float64、complex64和complex128。除了bool和string之外的其他基本类型也叫做数值类型。

复合类型一般由若干(也包括零)个其他已被定义的类型组合而成。复合类型有Channel(通道)、Function(函数)、Interface(接口)、Map(字典)、Struct(结构体)、Slice(切片)、Array(数组)和Pointer(指针)。

Go语言中的类型又可以分为静态类型和动态类型。一个变量的静态类型是指在变量声明中给出的那个类型。绝大多数类型的变量都只有静态类型。唯独接口类型的变量例外,它除了拥有静态类型之外,还拥有动态类型(接口类型在后面会讲到)。

每一个类型都会有一个潜在类型。如果这个类型是一个预定义类型(也就是基本类型),或者是一个由类型字面量构造的复合类型,那么它的潜在类型就是它自身。如string类型的潜在类型就是string类型,上面自定义的Person类型的潜在类型就是Person。如果一个类型并不属于上述情况,那么这个类型的潜在类型就是类型声明中的那个类型的潜在类型。

如下声明一个自定义类型

type MyString string1.
登入後複製

如上可以把类型MyString看作string类型的一个别名类型,那么MyString类型的潜在类型就是string类型。Go语言基本数据类型中的rune类型可以看作是uint32类型的一个别名类型,其潜在类型就是uint32。

注意:

  • 类型MyString和类型string是两个不相同的类型。不能将其中一个类型的值赋给另一个类型的变量。
  • 别名类型与它的源类型的不同仅仅体现在名称上,它们的内部结构是一致的;下面的类型转换的表达式都是合法的:MyString(“ABC”) 和string(MyString(“ABC”))。这种类型转换并不会创建新的值。

一个类型的潜在类型具有可传递性,如下:

type iString MyString1.
登入後複製

则类型isString的潜在类型就是string类型。

这里声明一个类型,如下:

type MyStrings [3]string1.
登入後複製

**注意:**類型MyStrings的潛在類型並不是[3]string。 [3]string既不是一個預先定義的型別,也不是由型別字面量建構的複合型別,而是一個元素型別為string的陣列型別。

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

Go語言規定,一個陣列類型的潛在類型決定了在該類型的變數中可以存放哪一個類型的元素。

5、運算子

運算子就是用來執行特定算術運算或邏輯運算的符號。 (這裡不詳細講解了,跟C語言的操作符類似),不過Go語言中沒有三元操作符,所以除了一元操作符以外都必定是二元操作符。 Go語言一共有21個操作符,包括算術運算子、比較運算子、邏輯運算子、位址運算子和接收運算子。

##相等判斷運算。二元,比較運算子「abc」 == “abc”//結果是true!=<#<=##小於或等於。二元,比較運算子1 <= 2 //表達式結果是true#>##大於判斷運算。二元,比較運算子##大於或等於。二元,比較運算子3 >= 2 //表達式結果是true#元,算術運算子 1 //結果為1 (1 2) //結果是3-表示求差,一元又是二元,算術運算子-1 //結果為-1 (1 – 2) //結果是-1\按位或操作,二元,算術運算子5 \ 11 //表達式的結果是15^按位元異或,一元又是二元,算術運算子55)//結果是-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//对应的二进制数运算1.2.3.
登入後複製

如上,内置函数uint8会将一个整数字面量转换为一个uint8类型的值,这保证了一元操作符^的唯一操作数一定是一个无符号整数类型的值。

(2). 操作是有符号的整数类型,这一操作就相当于对这个操作数和-1进行二元按位异或操作。例如:

^1                  = -2 
//有符号整数的一元按位异或操作00000001 ^ 11111111 = 11111110//对应的二进制运算1.2.
登入後複製

**注意:**以上的操作数的二进制值都是以补码形式表示;默认情况下整数字面量是有符号的,所以(2)中操作数1不需要显示使用内置函数int8 。

3、<- 接收操作符,只作用于通道类型的值。使用时,需要注意两点:

(1). 从一个通道类型的空值(即nil)接收值的表达式将会永远被阻塞。
(2). 从一个已被关闭的通道类型值接收值会永远成功并立即返回一个其元素类型的零值。

一个由接收操作符和通道类型的操作数所组成的表达式可以直接被用于变量赋值或初始化,如下所示(在赋值语句讲解时,再细说)

v1 := <-ch
v2 = <-ch1.2.
登入後複製

特殊标记 = 用于将一个值赋给一个已被声明的变量或常量。
特殊标记 := 则用于在声明一个变量的同时对这个变量进行赋值,且只能在函数体内使用。

又如下:

v, ok = <-ch
v, ok := <-ch1.2.
登入後複製

当同时对两个变量进行赋值或初始化时,第二个变量将会是一个布尔类型的值。这个值代表了接收操作的成功与否。如果这个值为false,就说明这个通道已经被关闭了。(之后讲解通道类型会详细介绍)。

操作符优先级

符號說明範例



#&&邏輯與運算。二元,邏輯運算子true && false //表達式結果是false
#==
##不等判斷運算。二元,比較運算子「abc」 != “Abc”//結果是true
#小於判斷運算。二元,比較運算子1 < 2 //表達式結果是true
3 > 2 //表達式結果是true#>=
11//結果是14( *
##& 按位與操作,一元,二元,算術,位址
&^ #位元清除操作,二元,算術運算子
优先级操作符
5* / % << >> & &^
4+ - \ ^
3== != < <= > >=
2&&
1

扩展知识:表达式

基本表达式

(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]1.
登入後複製

如上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)1.
登入後複製

该表达式的作用是判断“x不为nil且存储在其中的值是T类型的”是否成立。

如果T不是一个接口类型,那么x.(T)会判断类型T是否为x的动态类型(一个变量的动态类型就是在运行期间存储在其中的值的实际类型);而这个实际类型必须是该变量声明的那个类型的一个实现类型,否则就根本不可能在该变量中存储这一类型的值。所以类型T必须为x的类型的一个实现类型,而在Go语言中只有接口类型可以被其他类型实现,所以x的求值结果必须是一个接口类型的值。

所以上面表达式interface{}(num).(int)中表达式interface{}(num)的含义就是将变量num转换为interface{}类型的值(即它的结果值是接口类型的),而这刚好符合前面的定义。

知识点: interface{}是一个特殊的接口类型,代表空接口。所有类型都是它的实现类型。

在对变量的赋值或初始化的时候,也可以使用类型断言,如下:

v, ok := x.(T)1.
登入後複製

当使用类型断言表达式同时对两个变量进行赋值时,如果类型断言成功,那么赋给第一个变量的将会是已经被转换为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) []string1.
登入後複製

针对此函数的调用表达式如下:

appendIfAbsent([]string(“A”,”B”,”C”),”C”,”B”,”A”)1.
登入後複製

其中,与可变参数t绑定的切片类型值为[]string{”C”,”B”,”A”},包含了实际参数”C”,”B”和”A”。

也可以直接把一个元素类型为T的切片类型值赋给…T类型的可变长参数,如下调用:

appendIfAbsent([]string(“A”,”B”,”C”), []string(”C”,”B”,”A”)...)1.
登入後複製

或者如果有一个元素类型为stirng的切片类型的变量s的话,如下调用:

appendIfAbsent([]string(“A”,”B”,”C”), s...)1.
登入後複製

对于将切片类型的变量赋给可变长参数的情况,Go语言不会专门创建一个切片类型值来存储其中的实际参数。因为,这样的切片类型值已经存在了,可变长参数t的值就是变量s的值。

【相关推荐:Go视频教程编程教学

以上是Go語言的詞法元素有幾類的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板