1、变量
Go语言的变量声明方式和c/c++语言不同。Go语言引入了var关键字。变量声明如下:
var v1 int//声明一个整型变量v1
var v2 string
var v3 [10]int //数组
var v4 []int//数组切片
var v5 struct { //结构体
f int
}
var v6 *int//指针
var v7 map[string] int//map,ley为string,value为int
var v8 func(a int) int
可以看到,变量的声明没有以分号 ; 作为结束符。var关键字也可一次定义多个变量:
var (
v1 int
v2 string
)
变量的初始化
var v1 int = 10 // 正确的使用方式1
var v2 = 10 // 正确的使用方式2,编译器可以自动推导出 v2的类型
v3 := 10 // 正确的使用方式3,编译器可以自动推导出 v3的类型
使用:= 可以减少代码的书写量,这对程序员来说是大大的好处啊
但在我们使用的过程中,不应该出现如下:
var i int
i := 2//错误的做法
变量赋值
var v10 int//先声明,再赋值
v10 = 123
i, j = 2, 3//Go支持多重赋值
由多重赋值我们可以很方便的交换两个数的值
i, j = j, i//编译器会先计算等号右边的值,再为左边的赋值
匿名变量
Go语言支持多返回值和匿名变量
func GetName() (firstName, lastName, nickName string) {
return "May", "Chan", "Chibi Maruko"
}
返回了firstName, lastName, nickName,这三个变量均为string类型
我们在接收函数返回的参数时,也时可以选着性的接收,使用 _ 来忽略参数
_, _, nickName := GetName()
常量
在Go语言中,常量是指编译期间就已知且不可改变的值。常量可以是数值类型(包括整型、浮点型和复数类型)、布尔类型、字符串类型等。
//以下为字面常量
-12
3.14159265358979323846 // 浮点类型的常量
3.2+12i // 复数类型的常量
true // 布尔类型的常量
"foo" // 字符串常量
常量的定义
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 无类型浮点常量
const (
size int64 = 1024
eof = -1 // 无类型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重赋值
const a, b, c = 3, 4, "foo"
// a = 3, b = 4, c = "foo", 无类型整型和字符串常量
如果常量在定义时没有限定类型,那么该常量 是一个 字面常量。常量定义的右值也可以是一个在编译期运算的常量表达式,如:
由于常量的赋值是一个编译期行为,所以右值不能出现任何需要运行期才能得出结果的表达式,比如试图以如下方式定义常量就会导致编译错误:
const Home = os.GetEnv("HOME")
预定义常量
Go语言预定义了这些常量: true、 false和iota。
iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1。
从以下的例子可以基本理解iota的用法:
const ( // iota被重设为 0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota在每个const开头被重设为 0)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)
const (
u = iota * 42 // u == 0
v float64 = iota * 42 // v == 42.0
w = iota * 42 // w == 84
)
const x = iota // x == 0 ( 因为 iota又被重设为 0了 )
const y = iota // y == 0 ( 同上)
如果两个const的赋值语句的表达式是一样的,那么可以省略后一个赋值表达式。因此,上面的前两个const语句可简写为:
const ( // iota被重设为 0
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)
const (
a = 1 <
枚举
枚举指一系列相关的常量,比如下面关于一个星期中每天的定义。通过上一节的例子,我们看到可以用在const后跟一对圆括号的方式定义一组常量,这种定义法在Go语言中通常用于定义枚举值。 Go语言并不支持众多其他语言明确支持的enum关键字。
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 这个常量没有导出
)
同Go语言的其他符号( symbol)一样,以
大写字母开头的常量在包外可见。以上例子中numberOfDays为包内私有,其他符号则可被其他包访问。
类型
Go语言内置以下这些基础类型:
布尔类型: bool。
整型: int8、 byte、 int16、 int、 uint、 uintptr等。
浮点类型: float32 、 float64。
复数类型: complex64、 complex128。
字符串: string。
字符类型: rune。
错误类型: error。
此外, Go语言也支持以下这些复合类型:
指针( pointer)
数组( array)
切片( slice)
字典( map)
通道( chan)
结构体( struct)
接口( interface)
在这些基础类型之上Go还封装了下面这几种类型: int、 uint和uintptr等。这些类型的特点在于使用方便,但使用者不能对这些类型的长度做任何假设。对于常规的开发来说,用int和uint就可以了,没必要用int8之类明确指定长度的类型,以免导致移植困难。
bool类型
Go语言中的布尔类型与其他语言基本一致,关键字也为bool,可赋值为预定义的true和false示例代码如下:
var v1 bool
v1 = true
v2 := (1 == 2) // v2也会被推导为 bool类型
布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换。以下的示例是一些错误的用法,会导致编译错误:
var b bool
b = 1 // 编译错误
b = bool(1) // 编译错误
以下的用法才是正确的:
var b bool
b = (1!=0) // 编译正确
fmt.Println("Result:", b) // 打印结果为 Result: true
整型
1. 类型表示
需要注意的是, int和int32 在Go语言里被认为是两种不同的类型,编译器也不会帮你自动做类型转换,比如以下的例子会有编译错误:
var value2 int32
value1 := 64 // value1将会被自动推导为 int类型
value2 = value1 // 编译错误
使用强制类型转换可以解决这个编译错误:
value2 = int32(value1) // 编译通过
当然,开发者在做强制类型转换时,需要注意数据长度被截短而发生的数据精度损失(比如将浮点数强制转为整数)和值溢出(值超过转换的目标类型的值范围时)问题。
2. 数值运算
Go语言支持下面的常规整数运算: +、 -、 *、 /和%。加减乘除就不详细解释了,需要说下的是, % 和在C语言中一样是求余运算,比如:
Go语言支持以下的几种比较运算符: >、 <、 ==、 >=、 <=和!=。这一点与大多数其他语言相同,与C语言完全一致。
下面为条件判断语句的例子:
i, j := 1, 2
if i == j {
fmt.Println("i and j are equal.")
}
两个不同类型的整型数不能直接比较,比如int8类型的数和int类型的数不能直接比较,但各种类型的整型变量都可以直接与字面常量( literal)进行比较,比如:
var i int32
var j int64
i, j = 1, 2
if i == j { // 编译错误
fmt.Println("i and j are equal.")
}
if i == 1 || j == 2 { // 编译通过
fmt.Println("i and j are equal.")
}
4. 位运算
Go语言的大多数位运算符与C语言都比较类似,除了取反在C语言中是~x,而在Go语言中是^x。
浮点型
浮点型用于表示包含小数点的数据,比如1.234就是一个浮点型数据。 Go语言中的浮点类型采用IEEE-754标准的表达方式。
1. 浮点数表示
Go语言定义了两个类型float32 和float64 ,其中float32 等价于C语言的float 类型,
float64等价于C语言的double类型。
在Go语言里,定义一个浮点数变量的代码如下:
var fvalue1 float32
fvalue1 = 12
fvalue2 := 12.0 // 如果不加小数点, fvalue2会被推导为整型而不是浮点型
对于以上例子中类型被自动推导的fvalue2 ,需要注意的是其类型将被自动设为float64,而不管赋给它的数字是否是用32位长度表示的。因此,对于以上的例子,下面的赋值将导致编译
错误:
fvalue1 = fvalue2
而必须使用这样的强制类型转换:
fvalue1 = float32(fvalue2)
2. 浮点数比较
因为浮点数不是一种精确的表达方式,所以像整型那样直接用==来判断两个浮点数是否相等是不可行的,这可能会导致不稳定的结果。
下面是一种推荐的替代方案:
import "math"
// p为用户自定义的比较精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
return math.Fdim(f1, f2) < p
}
复数类型
复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部( real),一个表示虚部( imag)。如果了解了数学上的复数是怎么回事,那么Go语言的复数就非常容易理解了。
1. 复数表示
复数表示的示例如下:
var value1 complex64 // 由2个float32构成的复数类型
value1 = 3.2 + 12i
value2 := 3.2 + 12i // value2 是complex128类型
value3 := complex(3.2, 12) // value3 结果同 value2
2. 实部与虚部
对于一个复数z = complex(x, y) ,就可以通过Go语言内置函数real(z) 获得该复数的实
部,也就是x,通过imag(z) 获得该复数的虚部,也就是y。
更多关于复数的函数,请查阅math/cmplx标准库的文档。
字符串
在Go语言中,字符串也是一种基本类型。相比之下, C/C++语言中并不存在原生的字符串类型,通常使用字符数组来表示,并以字符指针来传递。
Go语言中字符串的声明和初始化非常简单,举例如下:
var str string // 声明一个字符串变量
str = "Hello world" //字符串赋值
ch := str[0] // 取字符串的第一个字符
fmt.Printf("The length of \"%s\" is %d \n", str, len(str))
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
输出结果为:
The length of "Hello world" is 11
The first character of "Hello world" is H.
字符串的内容可以用类似于数组下标的方式获取,但与数组不同,字符串的内容不能在初始化后被修改,比如以下的例子:
str := "Hello world" // 字符串也支持声明时进行初始化的做法
str[0] = 'X' // 编译错误
在这个例子中我们使用了一个Go语言内置的函数len() 来取字符串的长度。这个函数非常有用,我们在实际开发过程中处理字符串、数组和切片时将会经常用到。
本节中我们还顺便示范了Printf() 函数的用法。有C语言基础的读者会发现, Printf() 函数的用法与C语言运行库中的printf() 函数如出一辙。读者在以后学习更多的Go语言特性时,可以配合使用Println() 和Printf() 来打印各种自己感兴趣的信息,从而让学习过程更加直观、有趣。
Go编译器支持UTF-8的源代码文件格式。这意味着源代码中的字符串可以包含非ANSI的字符,比如“Hello world. 你好,世界!”可以出现在Go代码中。但需要注意的是,如果你的Go代
码需要包含非ANSI字符,保存源文件时请注意编码格式必须选择UTF-8。特别是在Windows下一般编辑器都默认存为本地编码,比如中国地区可能是GBK编码而不是UTF-8,如果没注意这点在编译和运行时就会出现一些意料之外的情况。
字符串的编码转换是处理文本文档(比如TXT、 XML、 HTML等)非常常见的需求,不过可惜的是Go语言仅支持UTF-8和Unicode编码。对于其他编码, Go语言标准库并没有内置的编码转换支持。不过,所幸的是我们可以很容易基于iconv库用Cgo包装一个。这里有一个开源项目:
https://github.com/xushiwei/go-iconv。
1. 字符串操作
更多的字符串操作,请参考标准库strings包。
2. 字符串遍历
Go语言支持两种方式遍历字符串。一种是以字节数组的方式遍历:
str := "Hello, 世界"
n := len(str)
for i := 0; i < n; i++ {
ch := str[i] // 依据下标取字符串中的字符,类型为 byte
fmt.Println(i, ch)
}
这个例子的输出结果为:
0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 228
8 184
9 150
10 231
11 149
12 140
可以看出,这个字符串长度为13。尽管从直观上来说,这个字符串应该只有9个字符。这是因为每个中文字符在UTF-8中占3个字节,而不是1个字节。
另一种是以Unicode字符遍历:
str := "Hello, 世界"
for i, ch := range str {
fmt.Println(i, ch)//ch的类型为 rune
}
输出结果为:
0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 19990
10 30028
以Unicode字符方式遍历时,每个字符的类型是rune(早期的Go语言用int类型表示Unicode
字符),而不是byte。
字符类型
在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符串的单个字节的值;另一个是rune,代单个Unicode字符。
关于rune相关的操作,可查阅 Go标准库的unicode包。另外unicode/utf8 包也提供了UTF8和Unicode之间的转换。出于简化语言的考虑, Go语言的多数API都假设字符串为UTF-8编码。尽管Unicode字符在标准库中有支持,但实际上较少使用。