Go语言的语言符号又称为词法元素,共包括5类:标识符(identifier)、关键字(keyword)、操做符(operator)、分隔符(delimiter)、以及字面量(literal)。通常状况下,空格符、水平制表符、回车符和换行符都会被忽略,除非它们做为多个语言符号之间的分隔符的一部分。在Go语言中不须要显示地插入分号,在必要时,Go语言会自动为代码插入分号以进行语句分隔。编程
Go语言代码由若干个Unicode字符组成,Go语言的全部源代码都必须由Unicode编码规范的UTF-8编码格式进行编码(也就是说编写的Go语言源码文件必须是UTF-8编码格式的)。数组
Go语言的标识符是由若干字母(由Unicode编码便可)、下划线和数字组成的字符序列;该字符序列的第一个字符必须为字母。并发
注意:app
- 在Go语言代码中,每个标识符都必须在使用前进行声明。
- 一个声明将一个非空的标识符与一个常量、类型、变量、函数或代码包绑定在一块儿。
- 在同一个代码块中,不容许重复声明同一个标识符(除了赋值语句例外)。
- 在一个源码文件和一个代码包中的标识符都须要遵循此规则。
- 一个已被声明的标识符的做用域与其直接所属的代码块的范围相同。
严格来说,代码包声明语句并不算是一个声明。由于代码包名称并不会出如今任何一个做用域中。代码包声明语句的目的是为了鉴别若干源码文件是否属于同一个代码包,或者指定导入代码包时的默认代码包引用名称。框架
限定标识符用来访问其余代码包中的变量或类型。例如,当我须要访问代码包os中名为O_RDONLY的常量时,须要这样写os.O_RDONLY。编程语言
限定标识符可以使用,须要知足两个前提条件:ide
- 要访问的代码包必须被事先导入;
- 这个代码包中的标识符必须是可导出的。
一个可导出的标识符也须要知足两个前提条件:函数
- 标识符名称中的第一个字符必须为大写(Go语言根据标识符名称中的第一个字符的大小写来肯定这个标识符的访问权限的,当标识符名称的第一个字符为大写时,其访问权限为“公开的”,也就是该标识符能够被任何代码包中的任何代码经过限定标识符访问到;当标识符的第一个字符为小写时,其访问权限就是"包级私有的",也就是只有与该标识符同在一个代码包的代码才可以访问到它);
- 标识符必须是被声明在一个代码包中的变量或者类型的名称,或者是属于某个结构体类型的字段名称或方法的名称。
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"表明了一个标准库代码包的标识符。
关键字(也称为保留字)是被编程语言保留而不让编程人员做为标识符使用的字符序列。
类别 | 关键字 |
---|---|
程序声明 | 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语言代码中用到的字面量有如下3类:
构造各类自定义的复合数据类型的类型字面量。例如,下面表示一个名称为Person的自定义结构体类型:
type Person struct { Name string Age uint8 Address string }
表示复合数据类型的值的复合字面量,被用来构造类型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"}
一个类型肯定了一类值的集合,以及能够在这些值上施加的操做。类型能够由类型名称或者类型字面量指定,分为基本类型和复合类型,基本类型的名称能够表明其自身。
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语言规定,一个数组类型的潜在类型决定了在该类型的变量中能够存放哪个类型的元素。
操做符就是用于执行特定算术运算或逻辑操做的符号。(这里不详细讲解了,跟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). 操做数是无符号的整数类型,使用这一个操做就至关于对这个操做数和其整数类型的最大值进行二元的按位异或操做,以下:
^uint8(1) = 254 //无符号整数的一元按位异或操做 00000001 ^ 11111111 = 11111110//对应的二进制数运算
如上,内置函数uint8会将一个整数字面量转换为一个uint8类型的值,这保证了一元操做符^的惟一操做数必定是一个无符号整数类型的值。
(2). 操做是有符号的整数类型,这一操做就至关于对这个操做数和-1进行二元按位异或操做。例如:
^1 = -2 //有符号整数的一元按位异或操做 00000001 ^ 11111111 = 11111110//对应的二进制运算
注意:以上的操做数的二进制值都是以补码形式表示;默认状况下整数字面量是有符号的,因此(2)中操做数1不须要显示使用内置函数int8 。
一个由接收操做符和通道类型的操做数所组成的表达式能够直接被用于变量赋值或初始化,以下所示(在赋值语句讲解时,再细说)
v1 := <-ch v2 = <-ch
特殊标记 = 用于将一个值赋给一个已被声明的变量或常量。
特殊标记 := 则用于在声明一个变量的同时对这个变量进行赋值,且只能在函数体内使用。
又以下:
v, ok = <-ch v, ok := <-ch
当同时对两个变量进行赋值或初始化时,第二个变量将会是一个布尔类型的值。这个值表明了接收操做的成功与否。若是这个值为false,就说明这个通道已经被关闭了。(以后讲解通道类型会详细介绍)。
优先级 | 操做符 | |
---|---|---|
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]
如上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。