diff --git a/2.2.md b/2.2.md index 7f276039..30be3efa 100644 --- a/2.2.md +++ b/2.2.md @@ -243,19 +243,33 @@ array就是数组,它的定义如下`[n]type`,n表示数组的长度,type表 对数组的操作和其他语言类似,都是通过`[]`来进行读取和赋值 - var arr [10]int //申明了一个int类型的数组 arr[0] = 42 //数组下标是从0开始的 arr[1] = 13 //赋值操作 fmt.Printf("The first element is %d\n", arr[0]) //获取数据 - 由于数组的长度也是类型的一部分,比如`[3]int`与`[4]int`是不同的类型,所以数组是不能改变长度的,而且数组之间的赋值是值赋值,当把一个数组作为一个参数传入函数的时候,是这个数组的副本,而不是该数组的指针,如果要使用指针,那么就需要用到下面介绍的`slice`。 - 数组什么可以使用另一种`:=`来申明 - a := [3]int{1,2,3} //申明一个长度为3的数组 - b := [10]int{1,2,3} //申明了一个长度为10的数组,其中前面三个元素初始化为1、2、3,其他默认为0 - c := […]int{4,5,6} //可以省略长度,采用…,go会自动计算长度 -也许你会说,我想数组里面还是数组,能实现吗?当然咯,GO支持嵌套数组,即多维数组,如下代码申明了一个而二维数组 + var arr [10]int //申明了一个int类型的数组 + arr[0] = 42 //数组下标是从0开始的 + arr[1] = 13 //赋值操作 + fmt.Printf("The first element is %d\n", arr[0]) //获取数据 - //申明了一个二维数组,改数组是一个两个元素的数组,然后每个元素里面是4个int的元素 - double_array := [2][4]int {[4]int{1,2,3,4}, [4]int{5,6,7,8}} +由于数组的长度也是类型的一部分,比如`[3]int`与`[4]int`是不同的类型,所以数组是不能改变长度的,而且数组之间的赋值是值赋值,当把一个数组作为一个参数传入函数的时候,是这个数组的副本,而不是该数组的指针,如果要使用指针,那么就需要用到下面介绍的`slice`。 + +数组申明可以使用另一种`:=`来申明 + + a := [3]int{1,2,3} //申明一个长度为3的数组 + + b := [10]int{1,2,3} //申明了一个长度为10的数组,其中前面三个元素初始化为1、2、3,其他默认为0 + + c := […]int{4,5,6} //可以省略长度,采用…,go会自动计算长度 + +也许你会说,我想数组里面还是数组,能实现吗?当然咯,GO支持嵌套数组,即多维数组,如下代码申明了一个二维数组 + + //申明了一个二维数组,该数组是一个两个元素的数组,然后每个元素里面是4个int的元素 + double_array := [2][4]int {[4]int{1,2,3,4}, [4]int{5,6,7,8}} + //如果内部的元素和外部的一样,那么上面的申明可以简化,直接忽略内部的类型 - easy_array :=[2][4]int{{1,2,3,4},{5,6,7,8}} 数组的分配如下所示: ![](images/2.2.array.png?raw=true) - + easy_array :=[2][4]int{{1,2,3,4},{5,6,7,8}} + +数组的分配如下所示: + +![](images/2.2.array.png?raw=true) + ###slice @@ -263,7 +277,7 @@ array就是数组,它的定义如下`[n]type`,n表示数组的长度,type表 `slice`并不是真正意义上面的动态数组,而是一个引用类型,`slice`总是指向底层的一个`array`。`slice`的申明也可以像`array`一样,只要省略size - //和什么array一样,只是少了长度 + //和申明array一样,只是少了长度 var fslice []int 接下来我们可以申明一个slice,并初始化数据,如下所示 @@ -272,10 +286,10 @@ array就是数组,它的定义如下`[n]type`,n表示数组的长度,type表 slice可以从一个数组或者一个已经存在的slice里面再次申明,`slice`通过array[i:j]来获取,i是数组的开始位置,j是结束位置,但不包含array[j],他的长度是j-i - //申明一个含有是个字符元素的数组 + //申明一个含有10个字符元素的数组 var array [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} - //申明两个含有byte的slice + //申明三个含有byte的slice var a_slice,b_slice,c_slice []byte //a_slice指向数组的第2个元素开始,并到第五个元素结束, @@ -286,6 +300,8 @@ slice可以从一个数组或者一个已经存在的slice里面再次申明,` b_slice = array[3:5] // b_slice的元素是: array[3], array[4] +注意slice和数组申明时的区别:申明数组时,方括号内写明了数组的长度或使用`...`自动计算长度,而申明slice时,方括号内没有任何字符 + 他们的数据结构如下所示 ![](images/2.2.slice.png?raw=true) @@ -300,21 +316,22 @@ slice有一些简便的操作 //申明一个数组 var array [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} - //什么两个slice - var a_slice,b_slice []byte - + //申明两个slice + var a_slice,b_slice + // 演示一些简便操作 a_slice = array[:3] // 等价于a_slice = array[0:3] a_slice包含元素: a,b,c a_slice = array[5:] // 等价于a_slice = array[5:9] a_slice包含元素: f,g,h,i,j a_slice = array[:] // 等价于a_slice = array[0:9] 这样a_slice包含了全部的元素 //从slice获取slice - a_slice = array[3:7] // a_slice包含元素: d,e,f,g - b_slice = a_slice[1:3] //b_slice 包含a_slice[1], a_slice[2] 也就是含有: e,f - b_slice = a_slice[:3] //b_slice 包含 a_slice[0], a_slice[1], a_slice[2] 也就是含有: d,e,f - b_slice : a_slice[:] //b_slice包含所有a_slice的元素: d,e,f,g + a_slice = array[3:7] // a_slice包含元素: d,e,f,g,len=4,cap=8 + b_slice = a_slice[1:3] // b_slice 包含a_slice[1], a_slice[2] 也就是含有: e,f + b_slice = a_slice[:3] // b_slice 包含 a_slice[0], a_slice[1], a_slice[2] 也就是含有: d,e,f + b_slice = a_slice[0:5] // 对slice的slice可以在cap范围内扩展,此时b_slice包含:c,d,e,f,g + b_slice : a_slice[:] // b_slice包含所有a_slice的元素: d,e,f,g -slice是引用类型,所以当引用改变值的时候,那么其他的所有引用都会改变该值,例如上面的a_slice和b_slice,如果修改了a_slice的值,那么b_slice相对应的值也会改变。 +slice是引用类型,所以当引用改变其中项目的值的时候,那么其他的所有引用都会改变该值,例如上面的a_slice和b_slice,如果修改了a_slice中项目的值,那么b_slice相对应的值也会改变。 从概念上面来说slice想一个结构体,这个结构体包含了三个元素: - 一个指针,指向数组中slice指定的开始位置 @@ -335,22 +352,24 @@ slice下面有几个有用的内置函数 - append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice - copy 函数copy 从源slice src 复制元素到目标dst,并且返回复制的元素的个数 +注:append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 +但当slice中没有剩余空间((cap-len) == 0)时,此时将动态分配新的数组空间,返回的slice的数组指针将指向这个空间,而原数组的内容将保持不变,其它引用此数组的slice不受影响。 ###map map也就是python中字典的概念,它的格式`map[keyType]valueType` -我们看下面的代码,map的读取和设置也类似slice一样,通过来key操作,只是slice只有int的key,而map多了很多类型,可以是int,可以是string +我们看下面的代码,map的读取和设置也类似slice一样,通过key来操作,只是slice只有int的key,而map多了很多类型,可以是int,可以是string // 申明一个key是字符串,值为int的字典 var numbers map[string] int - //另一种map的什么方式 - numbers = make(map[string]int) + //另一种map的申明方式 + numbers := make(map[string]int) numbers["one"] = 1 //赋值 numbers["ten"] = 10 //赋值 numbers["three"] = 3 - fmt.Println("第三个数字是: ", numbers[3]) //读取数据 + fmt.Println("第三个数字是: ", numbers["three"]) //读取数据 // 打印出来如下: //第三个数字是: 3 @@ -362,7 +381,7 @@ map也就是python中字典的概念,它的格式`map[keyType]valueType` - 内置的len函数同样试用于map,返回map拥有的key的数量 - map的值可以很方便的修改,通过`numbers["one"]=11`可以很容易的把key为`one`的字典值改为11 -map的初始化可以通过key:val的方式初始化值,同时获取map内置有判断是否存在key的方式 +map的初始化可以通过key:val的方式初始化值,同时map内置有判断是否存在key的方式 删除map的元素通过delete @@ -381,7 +400,7 @@ map的初始化可以通过key:val的方式初始化值,同时获取map内置 上面说过了,map也是一种引用类型,如果两个map同时指向一个底层,那么一个改变,另一个也相应的改变 - m = make(map[string][string]) + m = make(map[string]string) m["Hello"] = "Bonjour" m1 = m m1["Hello"] = "Salut" //现在m["hello"]的值已经是Salut了 @@ -393,8 +412,11 @@ map的初始化可以通过key:val的方式初始化值,同时获取map内置 内建函数new 本质上说跟其他语言中的同名函数功能一样:new(T) 分配了零值填充的T 类型的内存空间,并且返回其地址,一个*T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:new返回指针。 -内建函数make(T, args)与new(T)有着不同的功能。它只能创建slice,map和channel,并且返回一个有初始值(非零)的T类型,而不是*T。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针,长度和容量的三项描述符;在这些项目被初始化之前,slice 为nil。对于slice,map 和channel,make 初始化了内部的数据结构,填充适当的 值。make返回初始化后的(非零)值。 - 下面这个图详细的解释了new和make之间的区别 ![](images/2.2.makenew.png?raw=true) +内建函数make(T, args)与new(T)有着不同的功能。它只能创建slice,map和channel,并且返回一个有初始值(非零)的T类型,而不是*T。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针,长度和容量的三项描述符;在这些项目被初始化之前,slice 为nil。对于slice,map 和channel,make 初始化了内部的数据结构,填充适当的值。make返回初始化后的(非零)值。 + +下面这个图详细的解释了new和make之间的区别 + +![](images/2.2.makenew.png?raw=true) diff --git a/2.3.md b/2.3.md index 8e069ab5..3e2e8591 100644 --- a/2.3.md +++ b/2.3.md @@ -13,7 +13,7 @@ Go里面if条件语法中不需要括号,如下代码所示 fmt.Println("x is less than 10") } -Go的if还有一个强大的地方就是条件里面允许什么一个变量,这个变量的作用域只能在该条件中,出了这个条件就不起作用了,如下所示 +Go的if还有一个强大的地方就是条件里面允许申明一个变量,这个变量的作用域只能在该条件中,出了这个条件就不起作用了,如下所示 // 计算获取值x,然后根据x返回的大小,判断是否大于10. if x := computed_value(); x > 10 { @@ -37,15 +37,25 @@ Go的if还有一个强大的地方就是条件里面允许什么一个变量, ###goto -Go有goto语句——明智的使用它。用goto跳转到一定是当前函数内定义的标签。例如假设这样一个循环: - func myfunc() { i := 0 Here: //这行的第一个词,以分号结束作为标签 println(i) i++ goto Here //跳转到Here去 } - 标签名是大小写敏感的。 +Go有goto语句——明智的使用它。用goto跳转到一定是当前函数内定义的标签。例如假设这样一个循环: + + func myfunc() { + i := 0 + Here: //这行的第一个词,以分号结束作为标签 + println(i) + i++ + goto Here //跳转到Here去 + } + +标签名是大小写敏感的。 + ###for go里面最强大的一个控制逻辑就是for,他即可以用来循环读取数据,又可以当作while来控制逻辑,还能迭代操作。它的语法如下 for expression1; expression2; expression3{ ... } + expression1、expression2、expression3都是表达式,其中expression1和expression3是变量申明或者函数调用返回值之类的,expression2是条件判断,expression1在循环开始之前调用,expression3在循环结束之后调用 一个例子比上面讲那么多更有用,那么我们看看下面的例子吧 @@ -90,6 +100,7 @@ expression1、expression2、expression3都是表达式,其中expression1和exp //break打印出来10、9、8、7、6 //continue打印出来10、9、8、7、6、4、3、2、1 +break 和 continue 还可以跟着标号,用来跳到多重循环中的外层循环 for可以用于读取slice和map的数据,配合range @@ -177,4 +188,4 @@ sExpr和expr1、expr2、expr3的类型必须一致。Go的switch非常灵活。 * 下一节: [高级类型](<2.4.md>) ## LastModified - * $Id$ \ No newline at end of file + * $Id$