在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/02.2.md 定义变量Go语言里面定义变量有多种方式。 使用 //定义一个名称为“variableName”,类型为"type"的变量 var variableName type 定义多个变量 //定义三个类型都是“type”的变量 var vname1, vname2, vname3 type 定义变量并初始化值 //初始化“variableName”的变量为“value”值,类型是“type” var variableName type = value 同时初始化多个变量 /* 定义三个类型都是"type"的变量,并且分别初始化为相应的值 vname1为v1,vname2为v2,vname3为v3 */ var vname1, vname2, vname3 type= v1, v2, v3 你是不是觉得上面这样的定义有点繁琐?没关系,因为Go语言的设计者也发现了,有一种写法可以让它变得简单一点。我们可以直接忽略类型声明,那么上面的代码变成这样了: /* 定义三个变量,它们分别初始化为相应的值 vname1为v1,vname2为v2,vname3为v3 然后Go会根据其相应值的类型来帮你初始化它们 */ var vname1, vname2, vname3 = v1, v2, v3 你觉得上面的还是有些繁琐?好吧,我也觉得。让我们继续简化: /* 定义三个变量,它们分别初始化为相应的值 vname1为v1,vname2为v2,vname3为v3 编译器会根据初始化的值自动推导出相应的类型 */ vname1, vname2, vname3 := v1, v2, v3 现在是不是看上去非常简洁了?
_, b := 34, 35 Go对于已声明但未使用的变量会在编译阶段报错,比如下面的代码就会产生一个错误:声明了 package main func main() { var i int } 常量所谓常量,也就是在程序编译阶段就确定下来的值,而程序在运行时无法改变该值。在Go程序中,常量可定义为数值、布尔值或字符串等类型。 它的语法如下: const constantName = value //如果需要,也可以明确指定常量的类型: const Pi float32 = 3.1415926 下面是一些常量声明的例子: const Pi = 3.1415926 const i = 10000 const MaxThread = 10 const prefix = "astaxie_" Go 常量和一般程序语言不同的是,可以指定相当多的小数位数(例如200位), 若指定給float32自动缩短为32bit,指定给float64自动缩短为64bit,详情参考链接 内置基础类型Boolean在Go中,布尔值的类型为 //示例代码 var isActive bool // 全局变量声明 var enabled, disabled = true, false // 忽略类型的声明 func test() { var available bool // 一般声明 valid := false // 简短声明 available = true // 赋值操作 } 数值类型整数类型有无符号和带符号两种。Go同时支持
浮点数的类型有 这就是全部吗?No!Go还支持复数。它的默认类型是 var c complex64 = 5+5i //output: (5+5i) fmt.Printf("Value is: %v", c) 字符串我们在上一节中讲过,Go中的字符串都是采用 //示例代码 var frenchHello string // 声明变量为字符串的一般方法 var emptyString string = "" // 声明了一个字符串变量,初始化为空字符串 func test() { no, yes, maybe := "no", "yes", "maybe" // 简短声明,同时声明多个变量 japaneseHello := "Konichiwa" // 同上 frenchHello = "Bonjour" // 常规赋值 } 在Go中字符串是不可变的,例如下面的代码编译时会报错:cannot assign to s[0] var s string = "hello" s[0] = 'c' 但如果真的想要修改怎么办呢?下面的代码可以实现: s := "hello" c := []byte(s) // 将字符串 s 转换为 []byte 类型 c[0] = 'c' s2 := string(c) // 再转换回 string 类型 fmt.Printf("%s\n", s2) Go中可以使用 s := "hello," m := " world" a := s + m fmt.Printf("%s\n", a) 修改字符串也可写为: s := "hello" s = "c" + s[1:] // 字符串虽不能更改,但可进行切片操作 fmt.Printf("%s\n", s) 如果要声明一个多行的字符串怎么办?可以通过 m := `hello world`
hello world 错误类型Go内置有一个 err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) } Go数据底层的存储下面这张图来源于Russ Cox Blog中一篇介绍Go数据结构的文章,大家可以看到这些基础类型底层都是分配了一块内存,然后存储了相应的值。 图2.1 Go数据格式的存储 一些技巧分组声明在Go语言中,同时声明多个常量、变量,或者导入多个包时,可采用分组的方式进行声明。 例如下面的代码: import "fmt" import "os" const i = 100 const pi = 3.1415 const prefix = "Go_" var i int var pi float32 var prefix string 可以分组写成如下形式: import( "fmt" "os" ) const( i = 100 pi = 3.1415 prefix = "Go_" ) var( i int pi float32 prefix string ) iota枚举Go里面有一个关键字 package main import ( "fmt" ) const ( x = iota // x == 0 y = iota // y == 1 z = iota // z == 2 w // 常量声明省略值时,默认和之前一个值的字面相同。这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用"= iota" ) const v = iota // 每遇到一个const关键字,iota就会重置,此时v == 0 const ( h, i, j = iota, iota, iota //h=0,i=0,j=0 iota在同一行值相同 ) const ( a = iota //a=0 b = "B" c = iota //c=2 d, e, f = iota, iota, iota //d=3,e=3,f=3 g = iota //g = 4 ) func main() { fmt.Println(a, b, c, d, e, f, g, h, i, j, x, y, z, w, v) }
Go程序设计的一些规则Go之所以会那么简洁,是因为它有一些默认的行为:
array、slice、maparray
var arr [n]type 在 var arr [10]int // 声明了一个int类型的数组 arr[0] = 42 // 数组下标是从0开始的 arr[1] = 13 // 赋值操作 fmt.Printf("The first element is %d\n", arr[0]) // 获取数据,返回42 fmt.Printf("The last element is %d\n", arr[9]) //返回未赋值的最后一个元素,默认返回0 由于长度也是数组类型的一部分,因此 数组可以使用另一种 a := [3]int{1, 2, 3} // 声明了一个长度为3的int数组 b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0 c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式,Go会自动根据元素个数来计算长度 也许你会说,我想数组里面的值还是数组,能实现吗?当然咯,Go支持嵌套数组,即多维数组。比如下面的代码就声明了一个二维数组: // 声明了一个二维数组,该数组以两个数组作为元素,其中每个数组中又有4个int类型的元素 doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}} // 上面的声明可以简化,直接忽略内部的类型 easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}} 数组的分配如下所示: 图2.2 多维数组的映射关系 slice在很多应用场景中,数组并不能满足我们的需求。在初始定义数组时,我们并不知道需要多大的数组,因此我们就需要“动态数组”。在Go里面这种数据结构叫
// 和声明array一样,只是少了长度 var fslice []int 接下来我们可以声明一个 slice := []byte {'a', 'b', 'c', 'd'}
// 声明一个含有10个元素元素类型为byte的数组 var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // 声明两个含有byte的slice var a, b []byte // a指向数组的第3个元素开始,并到第五个元素结束, a = ar[2:5] //现在a含有的元素: ar[2]、ar[3]和ar[4] // b是数组ar的另一个slice b = ar[3:5] // b的元素是:ar[3]和ar[4]
它们的数据结构如下所示 图2.3 slice和array的对应关系图 slice有一些简便的操作
下面这个例子展示了更多关于 // 声明一个数组 var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // 声明两个slice var aSlice, bSlice []byte // 演示一些简便操作 aSlice = array[:3] // 等价于aSlice = array[0:3] aSlice包含元素: a,b,c aSlice = array[5:] // 等价于aSlice = array[5:10] aSlice包含元素: f,g,h,i,j aSlice = array[:] // 等价于aSlice = array[0:10] 这样aSlice包含了全部的元素 // 从slice中获取slice aSlice = array[3:7] // aSlice包含元素: d,e,f,g,len=4,cap=7 bSlice = aSlice[1:3] // bSlice 包含aSlice[1], aSlice[2] 也就是含有: e,f bSlice = aSlice[:3] // bSlice 包含 aSlice[0], aSlice[1], aSlice[2] 也就是含有: d,e,f bSlice = aSlice[0:5] // 对slice的slice可以在cap范围内扩展,此时bSlice包含:d,e,f,g,h bSlice = aSlice[:] // bSlice包含所有aSlice的元素: d,e,f,g
从概念上面来说
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} Slice_a := Array_a[2:5] 上面代码的真正存储结构如下图所示 图2.4 slice对应数组的信息 对于
注: 从Go1.2开始slice支持了三个参数的slice,之前我们一直采用这种方式在slice或者array基础上来获取一个slice var array [10]int slice := array[2:4] 这个例子里面slice的容量是8,新版本里面可以指定这个容量 slice = array[2:4:7] 上面这个的容量就是 如果slice是这样的形式 map
我们看下面的代码, // 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化 var numbers map[string]int // 另一种map的声明方式 numbers = make(map[string]int) numbers["one"] = 1 //赋值 numbers["ten"] = 10 //赋值 numbers["three"] = 3 fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据 // 打印出来如:第三个数字是: 3 这个 使用map过程中需要注意的几点:
通过 // 初始化一个字典 rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 } // map有两个返回值,第二个返回值,如果不存在key,那么ok为false,如果存在ok为true csharpRating, ok := rating["C#"] if ok { fmt.Println("C# is in the map and its rating is ", csharpRating) } else { fmt.Println("We have no rating associated with C# in the map") } delete(rating, "C") // 删除key为C的元素 上面说过了, m := make(map[string]string) m["Hello"] = "Bonjour" m1 := m m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了 make、new操作
内建函数
内建函数
下面这个图详细的解释了 图2.5 make和new对应底层的内存分配 零值关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值,通常为0。 此处罗列 部分类型 的 “零值” int 0 int8 0 int32 0 int64 0 uint 0x0 rune 0 //rune的实际类型是 int32 byte 0x0 // byte的实际类型是 uint8 float32 0 //长度为 4 byte float64 0 //长度为 8 byte bool false string "" |
请发表评论