Go语言的词法元素有5类:1、标识符,由若干字母(由Unicode编码即可)、下划线和数字组成的字符序列;2、关键字,是被编程语言保留而不让编程人员作为标识符使用的字符序列,也可以称为保留字;3、操作符,是用于执行特定算术运算或逻辑操作的符号;4、分隔符;5、字面量,是值的一种标记法。
本教程操作环境: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编码格式的)。
Go语言的标识符是由若干字母(由Unicode编码即可)、下划线和数字组成的字符序列;该字符序列的第一个字符必须为字母。
注意:
在Go语言代码中,每一个标识符都必须在使用前进行声明。
一个声明将一个非空的标识符与一个常量、类型、变量、函数或代码包绑定在一起。
在同一个代码块中,不允许重复声明同一个标识符(除了赋值语句例外)。
在一个源码文件和一个代码包中的标识符都需要遵循此规则。
一个已被声明的标识符的作用域与其直接所属的代码块的范围相同。
严格来讲,代码包声明语句并不算是一个声明。因为代码包名称并不会出现在任何一个作用域中。代码包声明语句的目的是为了鉴别若干源码文件是否属于同一个代码包,或者指定导入代码包时的默认代码包引用名称。
限定标识符用来访问其他代码包中的变量或类型。例如,当我需要访问代码包os中名为O_RDONLY的常量时,需要这样写os.O_RDONLY。
限定标识符能够使用,需要满足两个前提条件:
要访问的代码包必须被事先导入;
这个代码包中的标识符必须是可导出的。
一个可导出的标识符也需要满足两个前提条件:
标识符名称中的第一个字符必须为大写(Go语言根据标识符名称中的第一个字符的大小写来确定这个标识符的访问权限的,当标识符名称的第一个字符为大写时,其访问权限为“公开的”,也就是该标识符可以被任何代码包中的任何代码通过限定标识符访问到;当标识符的第一个字符为小写时,其访问权限就是"包级私有的",也就是只有与该标识符同在一个代码包的代码才能够访问到它);
标识符必须是被声明在一个代码包中的变量或者类型的名称,或者是属于某个结构体类型的字段名称或方法的名称。
Go语言的预定义标识符:
Go语言中有一个空标识符,它由一个下划线表示,一般用于一个不需要引入一个新绑定的声明中。例如,当我们只想执行一下某个代码包中的初始化函数,而不需要使用这个代码包中的任何程序实体的时候,可以编写如下导入语句:
其中,"runtime/cgo"代表了一个标准库代码包的标识符。
关键字(也称为保留字)是被编程语言保留而不让编程人员作为标识符使用的字符序列。
类别 | 关键字 |
---|---|
程序声明 | 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语言并发编程。
简单来说,字面量就是值的一种标记法。但是,在Go中,字面量的含义要更加广泛一些。
Go语言代码中用到的字面量有以下3类:
1、表示基础数据类型值的各种字面量。例如,表示浮点数类型值的12E-3。
2、构造各种自定义的复合数据类型的类型字面量。例如,下面表示一个名称为Person的自定义结构体类型:
3、表示复合数据类型的值的复合字面量
被用来构造类型Struct(结构体)、Array(数组)、Slice(切片)和Map(字典)的值。例如,下面的字面量用于表示上面名称为Person的结构体类型的值:
注意:
对复合字面量的每次求值都会导致一个新的值被创建。因此,如上该复合字面量每被求值一次就会创建一个新的Person类型的值。
Go语言不允许在一个此类的复合字面变量中,出现重复的键。如下都是错误,无法通过编译,因为键都有重复。
一个类型确定了一类值的集合,以及可以在这些值上施加的操作。类型可以由类型名称或者类型字面量指定,分为基本类型和复合类型,基本类型的名称可以代表其自身。
如上声明了一个类型为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。如果一个类型并不属于上述情况,那么这个类型的潜在类型就是类型声明中的那个类型的潜在类型。
如下声明一个自定义类型
如上可以把类型MyString看作string类型的一个别名类型,那么MyString类型的潜在类型就是string类型。Go语言基本数据类型中的rune类型可以看作是uint32类型的一个别名类型,其潜在类型就是uint32。
注意:
一个类型的潜在类型具有可传递性,如下:
则类型isString的潜在类型就是string类型。
这里声明一个类型,如下:
**注意:**类型MyStrings的潜在类型并不是[3]string。[3]string既不是一个预定义的类型,也不是一个由类型字面量构造的复合类型,而是一个元素类型为string的数组类型。
根据上面的定义可知类型MyStrings的潜在类型就是[3]string的潜在类型string。
Go语言规定,一个数组类型的潜在类型决定了在该类型的变量中可以存放哪一个类型的元素。
操作符就是用于执行特定算术运算或逻辑操作的符号。(这里不详细讲解了,跟C语言的操作符类似),不过Go语言中没有三元操作符,所以除了一元操作符以外都必定是二元操作符。Go语言一共有21个操作符,包括算术操作符、比较操作符、逻辑操作符、地址操作符和接收操作符。
符号 | 说明 | 示例 |
---|---|---|
&& | 逻辑与操作。二元,逻辑操作符 | true && false //表达式结果是false |
== | 相等判断操作。二元,比较操作符 | “abc” == “abc”//结果是true |
!= | 不等判断操作。二元,比较操作符 | “abc” != “Abc”//结果是true |
89d76cdeb80d41655512e16218266dfb | 大于判断操作。二元,比较操作符 | 3 > 2 //表达式结果是true |
>= | 大于或等于。二元,比较操作符 | 3 >= 2 //表达式结果是true |
+ | 表示求和,一元又是二元,算术操作符 | +1 //结果为1 (1+2) //结果是3 |
- | 表示求差,一元又是二元,算术操作符 | -1 //结果为-1 (1 – 2) //结果是-1 |
\ | 按位或操作,二元,算术操作符 | 5 \ 11 //表达式的结果是15 |
^ | 按位异或,一元又是二元,算术操作符 | 511//结果是14(5)//结果是-6 |
* | 求积或取值,一元,二元,算术,地址 | *p //取值操作 |
/ | 求商操作,二元,算术操作符 | 10 / 5 //表达式的结果为2 |
% | 求余数操作,二元,算术操作符 | 12 % 5 //表达式的结果为2 |
84403b5ba4b046b5dad37535a28457ce> | 按位右移操作,二元,算术操作符 | 4 >> 2 //表达式的结果为1 |
& | 按位与操作,一元,二元,算术,地址 | &v //取地址操作 |
&^ | 按位清除操作,二元,算术操作符 | 5 &^ 11 //表达式的结果为4 |
! | 逻辑非操作,一元,逻辑操作符 | !b //若b为true,结果为false |
5b75e59e7a05e5b699971202cc46b6d3> & &^ | ||
4 | + - \ ^ | |
3 | == != 8056e466eb4ac2e45f0ee1fdc891a78a >= | |
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语言允许如下的赋值语句:
如上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不为nil且存储在其中的值是T类型的”是否成立。
如果T不是一个接口类型,那么x.(T)会判断类型T是否为x的动态类型(一个变量的动态类型就是在运行期间存储在其中的值的实际类型);而这个实际类型必须是该变量声明的那个类型的一个实现类型,否则就根本不可能在该变量中存储这一类型的值。所以类型T必须为x的类型的一个实现类型,而在Go语言中只有接口类型可以被其他类型实现,所以x的求值结果必须是一个接口类型的值。
所以上面表达式interface{}(num).(int)中表达式interface{}(num)的含义就是将变量num转换为interface{}类型的值(即它的结果值是接口类型的),而这刚好符合前面的定义。
知识点: interface{}是一个特殊的接口类型,代表空接口。所有类型都是它的实现类型。
在对变量的赋值或初始化的时候,也可以使用类型断言,如下:
当使用类型断言表达式同时对两个变量进行赋值时,如果类型断言成功,那么赋给第一个变量的将会是已经被转换为T类型的表达式x的求值结果,否则赋给第一个变量的就是类型T的零值。布尔类型会被赋给变量ok,它体现了类型断言的成功(true)与否(false)。
注意: 在这种场景下,即使类型断言失败也不会引发运行时恐慌。
(8) 一个基本表达式和一个调用符号组成。
调用符号只针对于函数或者方法。与调用符号组合的基本表达式不是一个代表代码包名称(或者其别名)的标识符就是一个代表结构体类型的方法的名称的标识符。调用符号由一个英文句号为前缀和一个被圆括号括起来的参数列表组成,多个参数列表之间用逗号分隔。例如,基本表达式os.Open(“/etc/profile”)表示对代码包os中的函数Open的调用。
如果函数f可以接受的参数的数量是不固定的,那么函数f就是一个能够接受可变长参数的函数,简称可变参函数。在Go语言中,在可变参函数的参数列表的最后总会出现一个可变长参数,这个可变长参数的类型声明形如…T。Go语言会在每次调用函数f的时候创建一个切片类型值,并用它来存放这些实际函数。这个切片类型值的长度就是当前调用表达式中与可变长参数绑定的实际参数的数量。
可变参函数appendIfAbsent声明如下(函数体省略):
针对此函数的调用表达式如下:
其中,与可变参数t绑定的切片类型值为[]string{”C”,”B”,”A”},包含了实际参数”C”,”B”和”A”。
也可以直接把一个元素类型为T的切片类型值赋给…T类型的可变长参数,如下调用:
或者如果有一个元素类型为stirng的切片类型的变量s的话,如下调用:
对于将切片类型的变量赋给可变长参数的情况,Go语言不会专门创建一个切片类型值来存储其中的实际参数。因为,这样的切片类型值已经存在了,可变长参数t的值就是变量s的值。
以上是Go语言的词法元素有几类的详细内容。更多信息请关注PHP中文网其他相关文章!