Go基本语法
变量、数据类型和常量是编程中最常见,也是很好理解的概念。
变量
变量的功能是存储用户的数据。不同的逻辑有不同的对象类型,也就有不同的变量类型。经过半个多世纪的发展,编程语言已经形成一套固定的类型,这些类型在不同的编程语言中基本是相通的。常见变量的数据类型有:整型、浮点型、布尔型、结构体等。
Go语言作为C语言家族的新派代表,在C语言的定义方法和类型上做了优化和调整,更加灵活易学。
Go语言的每一个变量都拥有自己的类型,必须经过声明才能开始用。
声明变量
下面先通过一段代码了解变量声明的基本样式。
01 var a int
02 var b string
03 var c []float32
04 var d func() bool
05 var e struct{
06 x int
07 }
代码说明如下:
- 第1行,声明一个整型类型的变量,可以保存整数数值。
- 第2行,声明一个字符串类型的变量。
- 第3行,声明一个32位浮点切片类型的变量,浮点切片表示由多个浮点类型组成的数据结构。
- 第4行,声明一个返回值为布尔类型的函数变量,这种形式一般用于回调函数,即将函数以变量的形式保存下来,在需要的时候重新调用这个函数。
- 第5行,声明一个结构体类型的变量,这个结构体拥有一个整型的x字段。
上面代码的共性是,以var关键字开头,要声明的变量名放在中间,而将其类型放在后面。
变量的声明有几种形式。
- 标准格式
Go语言的变量声明格式为:
var 变量名 变量类型
变量声明以关键字var开头,后置变量类型,行尾无须分号。
- 批量格式
觉得每行都用var声明变量比较烦琐?没关系,还有一种为懒人提供的定义变量的方法:
var ( a int b string c []float32 d func() bool e struct { x int } )
使用关键字var和括号,可以将一组变量定义放在一起。
多个变量同时赋值
编程最简单的算法之一,莫过于变量交换。交换变量的常见算法需要一个中间变量进行变量的临时保存。用传统方法编写变量交换代码如下:
var a int = 100
var b int = 200
var t int
t = a
a = b
b = t
fmt.Println(a, b)
在计算机刚发明时,内存非常“精贵”。这种变量交换往往是非常奢侈的。于是计算机“大牛”发明了一些算法来避免使用中间变量:
var a int = 100
var b int = 200
a = a ^ b
b = b ^ a
a = a ^ b
fmt.Println(a, b)
这样的算法很多,但是都有一定的数值范围和类型要求。 到了Go语言时,内存不再是紧缺资源,而且写法可以更简单。使用Go的“多重赋值”特性,可以轻松完成变量交换的任务:
var a int = 100
var b int = 200
b, a = a, b
fmt.Println(a, b)
多重赋值时,变量的左值和右值按从左到右的顺序赋值。 多重赋值在Go语言的错误处理和函数返回值中会大量地使用。
例如,使用Go语言进行排序时就需要使用交换,代码如下:
01 type IntSlice []int
02
03 func (p IntSlice) Len() int { return len(p) }
04 func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
05 func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
代码说明如下:
- 第1行,将[]int声明为IntSlice类型。
- 第3行,为这个类型编写一个Len方法,提供切片的长度。
- 第4行,根据提供的i、j元素索引,获取元素后进行比较,返回比较结果。
- 第5行,根据提供的i、j元素索引,交换两个元素的值。
匿名变量——没有名字的变量
在使用多重赋值时,如果不需要在左值中接收变量,可以使用匿名变量。 匿名变量的表现是一个“_”下画线,使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。 例如:
01 func GetData() (int, int) {
02 return 100, 200
03 }
04
05 a, _ := GetData()
06
07 _, b := GetData()
08
09 fmt.Println(a, b)
代码输出如下:
100 200
GetData()是一个函数,拥有两个整型返回值。每次调用将会返回100和200两个数值。 代码说明如下:
- 第5行只需要获取第一个返回值,所以将第二个返回值的变量设为下画线。
- 第7行将第一个返回值的变量设为匿名。 匿名变量不占用命名空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。 提示:在Lua等编程语言里,匿名变量也被叫做哑元变量。
数据类型
Go语言中有丰富的数据类型,除了基本的整型、浮点型、布尔型、字符串外,还有切片、结构体、函数、map、通道(channel)等。Go语言的基本类型和其他语言大同小异,切片类型有着指针的便利性,但比指针更为安全,很多高级语言都配有切片进行安全和高效率的内存操作。 结构体是Go语言基础的复杂类型之一。 函数也是Go语言的一种数据类型,可以对函数类型的变量进行赋值和获取。 map和切片是开发中常见的数据容器类型。 通道与并发息息相关。
整型
整型分为以下两个大类。
- 按长度分为:
int8
、int16
、int32
、int64
-
还有对应的无符号整型:
uint8
、uint16
、uint32
、uint64
。 其中,uint8就是我们熟知的byte型,int16对应C语言中的short型,int64对应C语言中的long型。 -
自动匹配平台的int和uint Go语言也有自动匹配特定平台整型长度的类型——int和uint。 可以跨平台的编程语言可以运行在多种平台上。平台的字节长度是有差异的。64位平台现今已经较为普及,但8位、16位、32位的操作系统依旧存在。16位平台上依然可以使用64位的变量,但运行性能和内存性能上较差。同理,在64位平台上大量使用8位、16位等与平台位数不等长的变量时,编译器也是尽量将内存对齐以获得最好的性能。 不能正确匹配平台字节长度的程序就类似于用轿车运一头牛和用一辆卡车运送一头牛的情形一样。 在使用int和uint类型时,不能假定它是32位或64位的整型,而是考虑int和uint可能在不同平台上的差异。
- 哪些情况下使用int和uint 逻辑对整型范围没有特殊需求。例如,对象的长度使用内建len()函数返回,这个长度可以根据不同平台的字节长度进行变化。实际使用中,切片或map的元素数量等都可以用int来表示。
反之,在二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用int和uint。
浮点型
Go语言支持两种浮点型数:float32
和float64
。这两种浮点型数据格式遵循IEEE 754标准。
- float32的浮点数的最大范围约为3.4e38,可以使用常量定义:math.MaxFloat32。
- float64的浮点数的最大范围约为1.8e308,可以使用一个常量定义:math.MaxFloat64。
打印浮点数时,可以使用fmt包配合动词“%f”,代码如下:
01 package main 02 03 import ( 04 "fmt" 05 "math" 06 ) 07 08 func main() { 09 10 fmt.Printf("%f\n", math.Pi) 11 12 fmt.Printf("%.2f\n", math.Pi) 13 }
代码说明如下:
- 第10行,按默认宽度和精度输出整型。
- 第12行,按默认宽度,2位精度输出(小数点后的位数)。
代码输出如下:
3.141593 3.14
布尔型
布尔型数据在Go语言中以bool类型进行声明,布尔型数据只有true(真)和false(假)两个值。 Go语言中不允许将整型强制转换为布尔型,代码如下:
var n bool
fmt.Println(int(n) * 2)
编译错误,输出如下:
cannot convert n (type bool) to type int
布尔型无法参与数值运算,也无法与其他类型进行转换。