golang语法学习(一):变量,常量以及数据类型

时间:2021-02-06 15:46:29

学习一门新的语言肯定是要从他的基本语法开始,语法构成了整个程序设计的基础,从语法中我们也可以看到这门语言的一些特性,但是话说回来,语法这东西,不同的语言大同小异,所以这也对语法的记忆造成了一定的难度,其实最好的方法应该是旁边有本书,随时可以拿过来查阅或者纠正。当然golang的语法也是这样,下面的一些是我学习七牛云存储团队许式伟等出的《Go语言编程》: 
http://baike.baidu.com/link?url=vFrjnVJwITN0LaRbL7TmWYpq5V8JLWzL_NycAQy6w0e7JXP6a4bGm61ge0gGMU6jNFQO_RnuDGEQPh7YK9w4S_ 
语法部分的时候整理的一些,这本书对于新手入门还是挺有帮助的。为了日后可以及时回顾,所以记录下来了。为了避免长博客带来的阅读疲劳,这里尽量精简内容和分段。 
本文主要包括: 
1. Go中变量的声明,定义,赋值的方式以及匿名变量的概念。 
2. Go中常量的定义和使用 
3. Go中支持的内置数据类型及用法 
主要就是这三部分,总体来说介绍了这么语言中最基本的元素,就像构成英文的26个英文字母一样。

1. Go中的变量


1.1 变量声明


在Go中变量的声明方式如下:

var 变量名 变量类型

其中var是一个go内置的关键字,用来进行“纯粹“的变量的声明,注意这里的纯粹的意思,它是有别于系统根据赋值时候的右值进行自动推断变量类型的一种方式,可以认为是定义一个绝对类型的变量。 
比如:

 var    v1    int    //定义一个整形v1
var v2 [10]int //定义一个整形数组v2
var v3 struct { //定义了一个结构体
...
f int
...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

等等。所以习惯了c,c++,Java等语言定义变量的同学还需要特殊记忆一下这个定义方式,不知道go为什么这样定义,我猜测可能是发明者之前使用的某种语言的习惯吧,但是我不知道是哪种,知道的读者也可以告诉我。(后来发现这里或许可以解释 为什么golang的语法是这样的: 
http://blog.golang.org/gos-declaration-syntax)还有就是没必要书写一条语句的分号了,当然加了也不会报错。 
同时为了避免重复书写var,也可以将多个变量定义在一起:

var {
v1 int
v2 string
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

1.2 变量的赋值,初始化

Go中变量的赋值很简单,就是在定义完一个变量后,对变量赋初始值,没有什么特殊的地方。比如:

var i int
i = 10 //定义之后对i赋初值
  • 1
  • 2
  • 1
  • 2

但是在go中有一个很大的改革就是,允许多重赋值,以往在C语言中,如果想对i,j两个变量赋值,需要两个等号,例如i=3, j=4;但是在go中可以直接写成:

i , j = 3, 4
 
 
  • 1
  • 1

很简单。如果你想交换i,j的值,也只需要写成:

i , j = j, i //这就是go创新的地方
 
 
  • 1
  • 1

变量的初始化和变量的赋值类似,只是在变量声明的时候直接给出赋值,在golang中支持三种初始化的方式:

var 变量 变量类型 = 初值 
var 变量 = 初值 
变量 := 初值 
例如下面:

var v1 int = 10  //最规矩的写法
var v2 = 10 //编译器自动推算v2类型
v3 := 10 //同时进行变量的声明和初始化工作
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

要特别注意”:=”这个在c,c++,java中都未曾出现过的符号,他可以缩短代码的行数,编译器应该是将这句话解释成为两句,var v3 int , v3 = 10 
所以”:=”的左侧要是一个没有被声明过的变量,否则会下面的错误, 要特别注意

no new variables on left side of :=

1.3 Golang中匿名变量的概念

用过matlab这种解释语言的读者应该对一个函数的多返回值有印象,在c,c++中如果要想返回多个值,要么是返回一个类型,结构体,或者在形参中传入要返回变量类型的指针。这些在golang中直接支持了函数多返回值这样一种方式,但是,如果多返回值中有些是我们不需要的那么怎么办,和matlab类似,这里引入了匿名变量,”_”,其实就是作为一种缺省。例如书中的列子:

func GetName() (firstName, lastName, nickName string) {
return "may", "chan", "haha"
}
_, _ ,nickname := GetNmae() //这里面就是一种缺省,值用来获得nickName
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2. Go中的常量

Go中也是用const来进行常量的定义,例如

const str string = "hellotest” //string类型
const number int32 = 1234 //32位整形常量等
const ( //枚举类型,减少const的书写
zero = 0.0
eof = -1
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

除此之外,还有预定义的变量,包括bool型”true”,”false”等以及自动增长的iota。 
其中iota在出现const的时候被置0,之后没出现一次iota自动加1,直到被重新置0为止。例如

const (                     //iota被重设为0
a = iota //a = 0
b = iota //b=1
c = iota //c=2
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

注意上面因为是枚举,可以简化为

const (                     //iota被重设为0
a = iota //a = 0
b //b=1
c //c=2
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

1.3 Go支持的类型

先来看看Go支持的基础类型,然后我们围绕一些类型来介绍go中首创的,新鲜的一些特性。 
和其他类型一样,go支持:

布尔型 bool 
整形 int8, byte, int16, int, uint, uintptr 
浮点类型: float32 (c中的float ), float64 ( c中的double ) 
复数类型: complex64, complex128(go中特有的) 
字符串: string (内置类型) 
字符类型: rune(Unicode字符类型), byte(UTF-8字符类型) 
错误类型: error 
以及复合类型: 
指针: pointer 数组: array 切片: alice 
字典: map 通道: chan 结构体: struct 接口: interface 
等 
下面我们分别介绍一下这些类型的使用注意事项:

1) bool类型,

在bool类型中,不像c中那样,对一个bool类型的变量只能赋值,true或者false。或者是比较运算。不能直接赋值0,1,也不能对0,1进行强制类型转换,例如

var v1 bool = true//正确
var b bool = (1==2) // 正确
var f1 bool = 1 // false
var f2 bool = bool(1) //错误
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2) 整形

在整形中,go分别支持8,16,32,64bit的有符号和无符号整形。其中 unit8 就是byte,类似于c语言中的char型。还有就是int和int32在go语言里是两种不同的类型,所以这两种类型的变量也不可以相互赋值以及进行运算。如果进行运算的话需要进行强制类型转换。同时go中的整形与C语言类似,也支持数值运算,比较运算,以及位运算。除了位运算中的取反是使用^x 以外,C语言是使用~x,其他和C语言完全一样。

3) 浮点型

在浮点型中,go中定义了float32, float64两种,其中float32等价于C中的float,float64等价于C语言中的double。同时,在Go中,定义一个浮点数变量的时候,如果没有显示声明类型,编译器在自动推算的时候会将变量声明为float64而不是float32,例如:

fvalue := 12.0 //这里fvalue有编译器自动识别为float64
 
 
  • 1
  • 1

还有就是,浮点数中的判断相等比较的时候,因为浮点数的精度问题,所以不能直接用“==”来进行两个变量的判断,在math库中提供了,math.Fdim(var1,var2)来对两个变量的差值进行判断。

4) 复数类型

复数类型是go中引入的一种新的内置的数据类型,其中complex64表示用实部和虚由float32构成,complex128类推。复数的可以有下面3中使用方法:

var value1 complex64 = 3.2 + 12i
value2 := 3.2 + 12i
value3 := complex(3.2, 12)
r = real(value1) //获得复数的实部
i = imag(value1) //获得复数的虚部
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

5) 字符串和字符类型

在go中,字符串是一种内置的类型,和C++中的包类似,可以使用数组下标的形式获取字符,但是注意,不能用这种方式对字符进行修改。例如下面的用法:

var str string           //声明一个string类型的变量
str = "String Test" //赋值
ch := str[0] //获取第一个元素的值
length = len(str) //字符串的长度
str[0] = 's' //出错XXXX。不能这样来赋值
str = str + "Haha" // 字符串的链接
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里还需要注意的是,golang中只支持UTF-8以及Unicode的编码,而对于其他的编码并没有内置的编码转换,所以在我们保存的时候需要很小心,尤其是中国地区可能默认的保存的编码方式是GBK,这里提供七牛云存储团队自己搞的一个转换的源码: https://github.com/xushiwei/go-iconv 
关于UTF-8的编码是用byte这种类型来定义的,Unicode是用rune来定义的。所以要注意字符的编码方式,尤其是在字符串遍历的时候,如果使用range关键字,那么使用的是rune的形式来遍历的。而以数组取值的方式是byte的形式。

6)数组与数组切片

golang中的数组和c语言中的数组在一定程度上是一样的,包括元素的访问等,都是从0到len-1; 
但是golang中的数组有几个新的特性,包括: 
a. 可以使用len获得数组的长度 
b. 可以使用range来遍历访问容器中的元素,类似于foreach.例如

for i, v := range array { 
fmt.Println("Array element ['', i , '']=", v)
} //两个返回值
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

c. golang中的数组是值类型,和c语言中的数组名代表的是首地址不同。所以在传递数组的时候,不会改变原数组中的值,并且因为是值传递,所以每次都会进行一次形参值得copy。而这明显降低了函数调用的效率和空间的浪费。所以在golang中还提供了数组切片的功能。 
数组切片(slice)可以认为是关联某个数组的一个数据结构,这个数据结构包含以下三部分:

其所关联的数组的指针; 
数组切片中数组的元素的个数;(即实际包含的数组元素的个数) 
数组切片分配了的存储空间;(含有多大的空间) 
下面是创建数组切片的三种方式:

/*
one way:基于原生数组创建
*/

var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var mySlice [ ]int = myArray[:5]
/*
second way: 使用make直接创建
*/

mySlice1 := make([ ]int,5) //出事元素个数是5,且都为0
mySlice2 := make([ ]int, 5, 10) //容量是10
mySlice3 := [ ]int{1,2,3,4,5} //元素是1,2,3,4,5的切片。
/*
third way: 基于已有的切片进行创建
*/

oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:3] // 基于oldSlice的前3个元素构建新数组切片
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

对数组切片的操作: 
append函数用来附加;copy函数用来进行复制。

mySlice2 := []int{8, 9, 10} // 给mySlice后面添加另一个数组切片
mySlice = append(mySlice, mySlice2...)//注意mySlice2后面的...不能省
//等价于:
mySlice = append(mySlice,8,9,10)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

7)map

map是golang中内置的一种数据类型,与C++ STL中的map不同的是,这里的map没有按照键值对排序。(不知道为什么要这样,知道的读者也可以告诉我为什么不排序,那样的话查找不是很慢吗?可能插入删除会快些,这里面是个trade off?) 
map的使用包含如下几个方面:

a. 声明和创建,使用map,make关键词 
var map变量名 map[key的类型] value的类型 = make (map[key的类型] value的类型,元素容量) 其中元素容量参数可省。 
例如: 
var myMap map[string] PersonInfo = make (map[string] PersonInfo,100)

b. 初始化和赋值 
可以在创建map变量的时候直接初始化, 
myMap = map[string] PersonInfo { 
“1234”: PersonInfo{“1”, “Jack”, “Room 101, …”}, 

或者直接给key赋值: 
myMap[“1234”] = PersonInfo{“1”, “Jack”, “Room 101, …”}

c. 元素的删除 
直接使用delete(map容器,key值)函数,删除容器内的元素。例如:delete(myMap, “1234”);

d. 元素的查找 
golang中的map查找直接通过数组访问的形式,例如: 
value, ok := myMap[“1234”] 
if ok { // 找到了 
// 处理找到的value 

可以看到,返回值是两个,其中value是待查找的元素的key存在情况下的值,ok表示是否找到。所以这里的查找可以节省之前STL中使用find的一些逻辑较多的代码。

总结

到此为止,学了golang中最基本的组成语言的元素,包括变量,常量,以及支持的类型等。 
下面我们将要学习golang语法中的语句组织形式,函数等。