go语言入门

  • 1.Go语言第一个程序HelloWorld
  • 2.数据类型
  • 3.基本数据类型
    • 3.1变量的定义
    • 3.2 变量定义时没用初始化
    • 3.3 变量定义时没有指定这个变量类型
    • 3.4 使用:=定义变量
    • 3.5匿名变量
    • 3.6 变量的作用域
  • 4.指针
    • 4.1指针声明和初始化
    • 4.2空指针
  • 5. 数组
    • 5.1数组的声明
    • 5.2数组的初始化
    • 5.3二维数组
  • 6.结构体
    • 6.1 结构体声明和定义
    • 6.2访问结构体
    • 6.3 结构体指针
    • 6.4结构体成员方法
    • 6.5匿名结构体
  • 7.字符串
  • 7.1字符串定义和初始化
    • 7.2字符串强制类型转换
    • 7.3字符串拼接
  • 8.切片Slice
    • 8.1 向切片当中添加元素
  • 8.2 子切片
  • 9.map
    • 9.1添加和删除key值
    • 9.2遍历map
  • 10.channel
    • 10.1 管道的使用
    • 10.2定义只读或者只写管道
    • 10.3遍历管道
  • 11.go语言里面的循环
    • 11.1 break、goto、continue

1.Go语言第一个程序HelloWorld

一般我们在学习一门语言的时候都是从helloworld开始学习的,下面我们就一起学习一下如何使用Go语言打印这个hello World.

package mainimport "fmt"func main()  {fmt.Println("hello world")
}

和我们学习C语言的时候很类似,GO语言的基本组成有”

  • 包的声明编写源文件时必须指明这个文件是属于那个包的,比如经常使用的package main.
  • 引入包其实就是告诉go语言的编译器这个程序需要使用包,比如我们打印使用的fmt,import "fmt"其实就是引入了fmt包。
  • 函数,和c语言相同,即是一个可以实现某一个功能的函数体,每一个可执行程序中必须拥有一个main函数。
  • 变量,Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字.
  • 注释,和c语言中的注释方式相同,可以在任何地方使用以 // 开头的单行注释。以 /* 开头,并以 */ 结尾来进行多行注释,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
  • 语句和表达式不需要像c/c++语言一样在后面添加这个分号

2.数据类型

在go语言当中,数据类型用于声明函数和变量。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。具体分类如下:

3.基本数据类型

3.1变量的定义

定义变量我们一般使用var关键字。具体格式为var 变量名字 变量类型。下面我们演示一个代码定义一个整型变量。

func main() {var a int =10//注意如果定义的时候不给初始值系统会给这个默认值fmt.Println(a)
}

3.2 变量定义时没用初始化

注意在go语言当中如果定义了一个变量如果没用初始化,则变量默认为零值。所谓的零值也就是变量没有做初始化时系统默认设置的值。下面让我们看看基本数据类型不初始化系统会赋那些零值。

3.3 变量定义时没有指定这个变量类型

在go语言当中如果定义变量时没有指定变量的类型,可以通过初始值推导出变量的类型。下面我看一下这个代码

  package mainimport "fmt"func main() {var a = 10//根据初始值推导a的类型fmt.Println(a)
}

3.4 使用:=定义变量

在go语言的函数内部我们可以使用:=来定义变量。使用的格式为typename:=value。这个一条语句包含了声明和初始化。

package mainimport "fmt"func main() {a:=10fmt.Println(a)
}

注意这个只能在函数内部这样,而定义这个全局变量需要使用的是var不能使用:=来定义,下面我们来看看如何定义这个全局变量

import "fmt"var (a    int     = 10b    float64 = 12.2name string  = "ksy"
)func main() {a := 10fmt.Println(a)
}

3.5匿名变量

什么是匿名变量?在go语言当中’_'代表的是这个匿名变量。它可以像其它标识符一样用于变量的声明和赋值。任何值赋给这个标识符都将会被抛弃,不能够再后续代码当中继续使用,在使用匿名变量时,只需要在变量声明的地方使用下划线进行替换即可。下面我们来看一个代码

func main() {a := 10fmt.Println(a)_ =a
}

注意:匿名变量不占用内存,匿名变量不会因为多次声明而无法使用,这点是需要注意的。

3.6 变量的作用域

作用域指的是已声明的标识符所表示的常量、类型、函数或者包在源代码中的作用范围,在此我们主要看一下go中变量的作用域,根据变量定义位置的不同,可以分为一下三个类型:
1.函数内定义的变量为局部变量,这种局部变量的作用域只在函数体内,函数的参数和返回值变量都属于局部变量。这种变量在存在于函数被调用时,销毁于函数调用结束后。
2.函数外定义的变量为全局变量,全局变量只需要在一个源文件中定义,就可以在所有源文件中使用,甚至可以使用import引入外部包来使用。全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。
3.函数定义中的变量成为形式参数,定义函数时函数名后面括号中的变量叫做形式参数(简称形参)。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值。形式参数会作为函数的局部变量来使用。

4.指针

4.1指针声明和初始化

和基础数据类型一样,在使用指针变量之前我们首先需要声明指针,基本格式为:var var_name *var-type .其中var_name是指针的名字,var-type 指的是指针的类型。下面我们来演示一下这个
如何来定义这个指针变量

func main() {var ip * int var fp * float32
}

上面分别定义了两个指针一个指向了整型一个指向了浮点型。指针的初始化就是取出相应变量的地址并对其进行赋值,具体步骤如下:

func main() {a := 10var ptr *int = &afmt.Println(*ptr)}

4.2空指针

当一个指针被定义后没有分配任何变量时,它的值为nil。也就是我们经常说的空指针,其概念和其它语言的null,NULL,nullptr是一样的。都是指这个零值或者空值

5. 数组

5.1数组的声明

go语言数组的声明需要指明元素的类型和元素的个数,这一点和c/c++是一样的,其基本格式如下:

var variable_name [SIZE] variable_type.这样我们就可以定义一个一维数组,下面我们写一段代码来举例:

func main() {var arr[10]int//定义一个数组
}

5.2数组的初始化

在go语言当中数组的初始化方式还是很多的,下面我们一个一个的来看看如何进行初始化:

1.直接初始化

func main() {var arr =[5]int{1,2,3,4,5}fmt.Println(arr)
}

2.直接通过字面量在声明数组的同时快速初始化数组:

func main() {arr := [5]int{1, 2, 3, 4, 5}fmt.Println(arr)
}

3.数组的长度不确定但是编译器可以通过元素的个数推断出数组的长度,在[]当中使用’…'就可以了,下面我们举一个列子

func main() {arr := [...]int{1, 2, 3, 4, 5}fmt.Println(arr)
}

4.数组的长度是确定的但是我们可以指定下标进行初始化.

func main() {arr := [5]int{1: 2, 4: 23}//注意其它位置编译器会初始化默认的零值fmt.Println(arr)
}

注意:
初始化数组当中{}里面元素的个数不能超过数组的长度。如果我们不指定数组的大小Go语言会根据元素的个数来设置数组的大小。

下面我们看看go语言当中的数组名的含义。我们在c语言当中学习到了数组名表示的是首元素的地址。而在go语言当中数组名仅仅表示整个数组。

所以在go语言当中一个数组变量被赋值或者传递时会复制整个数组过去,如果数组过大那么这个开销还是特别大的。所以了在这种情况下往往需要传递一个数组指针过去。在c语言当中我们也学习过数组指针,下面我们来看看这个数组指针如何来定义

func main() {arr := [5]int{1: 2, 4: 23}//注意其它位置编译器会初始化默认的零值var ptr =&arr//ptr此时是一个是数组指针}

数组指针了除了在作为参数传递时能够减少拷贝,还能利用其和for range来变量数组,下面就以上面这个数组为例来遍历数组:

func main() {arr := [5]int{1: 2, 4: 23}//注意其它位置编译器会初始化默认的零值var ptr = &arr //ptr此时是一个是数组指针for i, v := range ptr {fmt.Printf("%d :%d\n", i, v)}
}

具体关于go语言的循环语句我们在后面在做讨论

5.3二维数组

在c语言里面我们也学过这个二维数组,下面我们一起来看看这个go语言里面的二维数组如何定义并且初始化的。

func main() {var arr = [5][5]int{}fmt.Println(arr)
}

上面我们定义了一个5行5列的二维数组,当时我们没有指定这个每个元素的初始值所以了系统会默认初始化为零值。下面我们在来看看两种指定初始值的

func main() {var arr1= [5][5]int{{1, 2, 3,4,5}, {1, 4, 6,7,8}}//只初始化前两行var arr2=[...][4]int{{1},{2,3,5}}//编译器自动根据行数进行推导fmt.Println(arr1)fmt.Println(arr2)
}

下面我们来演示一段小的demo,各位老铁可以看看这个程序输出的是什么

package mainimport "fmt"func Update1(arr [5]int) {arr[0] = 100
}
func Update2(arr *[5]int) {arr[0] = 101
}func main() {arr := [5]int{1, 2, 3, 4, 5}Update1(arr)fmt.Printf("Update1:%d\n", arr[0])Update2(&arr)fmt.Printf("Update2:%d\n", arr[0])}

各位老铁可以看看这段代码输出的是什么?这段代码输出的是这个 1和101这是为什么呢?前面我们说过go语言中是这个值传递Update1是传值所以了这个会拷贝一份给它,函数当中arr发生改变不会影响外面的。方式二:是通过传递地址所以了函数内发生了改变会影响到外部的arr

6.结构体

通过上述数组的学习,我们就可以直接定义多个同类型的变量,但这往往也是一种限制,只能存储同一种类型的数据,而我们在结构体中就可以定义多个不同的数据类型。下面我们来看看go语言当中的结构体如何进行声明

6.1 结构体声明和定义

type user struct {id intscore float32enrollment time.Timename, addr string //多个字段类型相同时可以简写到一行里
}

有了这个结构体类型之后,我们可以使用这个结构体类型来定义这个结构体变量,下面我们来看看如何定义这个结构体变量

package mainimport ("fmt""time"
)type user struct {id         intscore      float32enrollment time.Timename, addr string //多个字段类型相同时可以简写到一行里
}func main() {var u user                   //声明,会用相应类型的默认值初始化struct里的每一个字段u = user{}                   //用相应类型的默认值初始化struct里的每一个字段u = user{id: 3, name: "zcy"} //赋值初始化u = user{4, 100.0, time.Now(), "zcy", "beijing"}//赋值初始化,可以不写字段名,但需要跟结构体定义里的字段顺序一致u.enrollment = time.Now() //给结构体的成员变量赋值}

6.2访问结构体

如果我们需要访问结构体我们需要使用点号来访问其个数和c语言当中结构体的访问是一样的。下面我们来看看如何这个代码即可

package mainimport ("fmt""time"
)type user struct {id         intscore      float32enrollment time.Timename, addr string //多个字段类型相同时可以简写到一行里
}func main() {var u user                   //声明,会用相应类型的默认值初始化struct里的每一个字段u = user{}                   //用相应类型的默认值初始化struct里的每一个字段u = user{id: 3, name: "zcy"} //赋值初始化u = user{4, 100.0, time.Now(), "zcy", "beijing"}//赋值初始化,可以不写字段名,但需要跟结构体定义里的字段顺序一致u.enrollment = time.Now() //给结构体的成员变量赋值fmt.Printf("id=%d, enrollment=%v, name=%s\n", u.id, u.enrollment, u.name)//访问结构体的成员变量
}

6.3 结构体指针

关于结构体指针的定义和申明同样可以套用前文中讲到的指针的相关定义,从而使用一个指针变量存放一个结构体变量的地址。

定义一个结构体变量的语法:var struct_pointer *Books。

这种指针变量的初始化和上文指针部分的初始化方式相同struct_pointer = &Book1,但是和c语言中有所不同,使用结构体指针访问结构体成员仍然使用.操作符。格式如下:struct_pointer.title由于比较的简单所以了在这里就不做过多的演示

6.4结构体成员方法

在这里需要特别注意的是在go语言当中函数和方法不太一样,有明确的概念区分函数不属于任何结构体类型也就是说函数没有接收者。而方法是属于某个结构体的

package mainimport ("fmt""time"
)type user struct {id         intscore      float32enrollment time.Timename, addr string //多个字段类型相同时可以简写到一行里
}//可以把user理解为hello函数的参数,即hello(u user, man string)
func (u user) hello(man string) {fmt.Println("hi " + man + ", my name is " + u.name)
}//函数里不需要访问user的成员,可以传匿名,甚至_也不传
func (_ user) think(man string) {fmt.Println("hi " + man + ", do you know my name?")
}func main() {var u user                   //声明,会用相应类型的默认值初始化struct里的每一个字段u = user{}                   //用相应类型的默认值初始化struct里的每一个字段u = user{id: 3, name: "zcy"} //赋值初始化u = user{4, 100.0, time.Now(), "zcy", "beijing"}//赋值初始化,可以不写字段名,但需要跟结构体定义里的字段顺序一致u.enrollment = time.Now() //给结构体的成员变量赋值fmt.Printf("id=%d, enrollment=%v, name=%s\n", u.id, u.enrollment, u.name)//访问结构体的成员变量
}

6.5匿名结构体

匿名结构体了从字面上来理解就是这个结构体没有这个名字,这也就意味着它只能使用一次

package main
var stu struct { //声明stu是一个结构体,但这个结构体是匿名的Name stringAddr string
}func main(){//注意匿名结构体只能使用一次stu.Name = "zcy"stu.Addr = "bj"
}

当然结构体当中也可以有这个匿名字段,我们在访问时可以直接使用这个类型进行访问和操作

7.字符串

在go语言当中字符串是一个不可改变的字节序列,字符串的底层其实就是byte数组,字符串和数组不同字符串里面的元素是不能修改的,是一个只读的字节数组。每个字符串的长度虽然固定但是字符串的长度并不是字符串类型的一部分。

7.1字符串定义和初始化

o语言字符串的底层结构在reflect.StringHeader中定义,具体如下:

type StringHeader struct {Data uintptrLen  int
}

也就是说字符串结构由两个信息组成:第一个是字符串指向的底层字节数组,第二个是字符串的字节的长度。字符串其实是一个结构体,因此字符串的赋值操作也就是reflect.StringHeader结构体的复制过程,并不会涉及底层字节数组的复制,所以我们也可以将字符串数组看作一个结构体数组。
字符串和数组类似,内置的len函数返回字符串的长度。
下面我们来看一下这个字符串是如何使用的

package main
import "fmt"
func main() {var str string = "ksy jia you"for i := 0; i < len(str); i++ {fmt.Printf("%c", str[i])}
}

至于go语言的循环在后面细细的说明

在c语言当中我们经常会遇到一些这个转义字符比如\n和"这些字符通常有这个特殊的含义。如果需要表示其原来的含义我们需要使用’'来进行转义。而在go语言当中我们可以这样做也可以使用这个
``将要包含的字符串,那么这个字符串就会按照原有的样子呈现出来不会进行任何转义。

func main() {var str string = `"hello \n ddddd"`fmt.Println(str)
}

7.2字符串强制类型转换

在go语言当中字符串可以将字符串强制类型转换为[]byte字节序列和[]rune序列。每个强制类型转换都有可能存在这个隐式类型转换。字符串和[]rune的转换要更为特殊一些,因为一般这种强制类型转换要求两个类型的底层内存结构要尽量一致,显然它们底层对应的[]byte和[]int32类型是完全不同的内部布局,因此这种转换可能隐含重新分配内存的操作

func main() {var arr []byte = []byte("abc")fmt.Println(arr)
}

7.3字符串拼接

在这里只介绍go语言当中常见的几种字符串拼接的方法。其它的老铁可以自行百度。

1.通过go语言原生的拼接方式+号

func main() {var str stringstr += "abc"fmt.Println(str)
}

这种方式使用起来是最简单的,很多语言都支持这种方式。在使用+操作符进行拼接时会遍历两个字符串并开辟一块新的空间存储这两个字符串。有大量的字符串时这种效率是非常的低的

2.通过字符串格式化函数进行拼接fmt.Sprintf

在go语言当中可以使用fmt.Sprintf进行字符串格式化所以可以使用这种方式进行拼接

func main() {str1 := "abc"str2 := "bcd"str3 := fmt.Sprintf("%s%s", str1, str2)fmt.Println(str3)}

Sprintf的原理我们在后面的文章当中会涉及在这里就不谈了,其效率也不高

3.通过strings.Builder

在go语言当中提供了一个专门操作字符串的库叫做strings,使用string.Builder方法可以进行字符串的拼接,可以通过里面的writeString方法进行字符串的拼接,使用方式如下:

func main() {sb := strings.Builder{}str1 := "kys"str2 := "liu yuan zhi"sb.WriteString(str1)sb.WriteString(str2)fmt.Println(sb.String())
}

其实了strings.Builder的实现原理非常的简单,下面我们来看看整个定义

type Builder struct {addr *Builder // of receiver, to detect copies by valuebuf  []byte // 1
}

addr字段主要是做copycheck,buf字段是一个byte类型的切片,这个就是用来存放字符串内容的,提供的writeString()方法就是向切片buf中追加数据:其提供的String方法无非就是将字节数组强制类型转化为字符串。

4.通过 strings.Join进行拼接

func main() {str := []string{"ksy", "liu yuanzhi"}s := strings.Join(str, "") //第二个参数表示的是拼接的字符串直接使用什么进行分隔fmt.Println(s)}

最后总结一下:
使用strings.builder进行字符串拼接都是高效的,不过要主要使用方法,记得调用grow进行容量分配,才会高效。strings.join的性能约等于strings.builder,在已经字符串slice的时候可以使用,未知时不建议使用,构造切片也是有性能损耗的;如果进行少量的字符串拼接时,直接使用+操作符是最方便也是性能最高的,可以放弃strings.builder的使用。不能一概而论,具体情况具体分析。

8.切片Slice

slice和数组一样内置len函数用来求这个数组的长度,同样的还内置这个cap函数。但是slice和数组不同的是slice的cap一定是大于等于len的不像数组一样len总是等于cap的,下面我们来看看slice在底层的结构到底咱啥样.

type slice struct { array unsafe.Pointer len int cap int
}

切片是一个结构体,包含三个成员变量,array指向一块连续的内存空间,cap表示这块内存的大小,len表示目前该内存里存储了多少元素。
当我们想定义声明一个切片时可以如下:在对切片本身赋值或参数传递时,和数组指针的操作方式类似,只是复制切片头信息·(reflect.SliceHeader),并不会复制底层的数据。对于类型,和数组的最大不同是,切片的类型和长度信息无关,只要是相同类型元素构成的切片均对应相同的切片类型

8.1 向切片当中添加元素

在go语言当中我们可以通过append函数往这个切片里面添加元素,如果空间不够了它在底层会自动的扩容。下面我们来演示一下这个往切片里面添加元素

func main() {var  arr[]intarr=append(arr,1)//往切片里面添加1个数据arr=append(arr,1,2,3)//往切片里面添加3个数据fmt.Println(arr)}

8.2 子切片

1.往尾部添加元素
通过指定起止下标,可以从大切片中截取一个子切片。 下面我们来演示一下什么是子切片

func main() {arr1 := []int{1, 2, 3, 4, 5}arr2 := arr1[1 : len(arr1)]//arr2就是一个子切片fmt.Println(arr2)
}

刚开始,子切片和母切片共享底层的内存空间,修改子切片会反映到母切片上,在子切片上执行append会把新元素放到母切片预留的内存空间上。当子切片不断执行append,耗完了母切片预留的内存空间,子切片跟母切片就会发生内存分离,此后两个切片没有任何关系。

下面我们就来举个列子


func main() {arr1 := []int{1, 2, 3, 4, 5}arr2 := arr1[1:len(arr1)] //arr2就是一个子切片fmt.Printf("arr1:%p arr2:%p\n", &arr1[1], &arr2[0])fmt.Println("##########################")arr2 = append(arr2, 1)fmt.Printf("arr1:%p arr2:%p", &arr1[1], &arr2[0])}

2.在切片开头位置添加元素或者切片

func main() {arr1 := []int{1, 2, 2, 3}arr2 := append([]int{0}, arr1...)//在头部添加一个元素fmt.Println(arr2)//在开头添加一个切片arr2 = append([]int{11, 12}, arr2...)fmt.Println(arr2)}

注意:注意:在开头一般都会导致内存的重新分配,而且会导致已有的元素全部复制1次。因此,从切片的开头添加元素的性能一般要比从尾部追加元素的性能差很多。

3.删除尾部元素和中间元素
首先我们来看看删除这个尾部元素

a = []int{1, 2, 3, ...}a = a[:len(a)-1]   // 删除尾部1个元素
a = a[:len(a)-N]   // 删除尾部N个元素

删除中间元素

a = []int{1, 2, 3, ...}
a = append(a[:i], a[i+1], ...)
a = append(a[:i], a[i+N:], ...)

比如我们需要删除第i个元素

9.map

map表的底层原理是哈希表,其结构体定义如下:

type Map struct {Key  *Type // Key typeElem *Type // Val (elem) typeBucket *Type // 哈希桶Hmap   *Type // 底层使用的哈希表元信息Hiter  *Type // 用于遍历哈希表的迭代器
}

其中的Hmap 的具体化数据结构如下:

type hmap struct {// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.// Make sure this stays in sync with the compiler's definition.count     int // map目前的元素数目flags     uint8 // map状态(正在被遍历/正在被写入)B         uint8  // 哈希桶数目以2为底的对数(哈希桶的数目都是 2 的整数次幂,用位运算来计算取余运算的值, 即 N mod M = N & (M-1)))noverflow uint16 //溢出桶的数目, 这个数值不是恒定精确的, 当其 B>=16 时为近似值hash0     uint32 // 随机哈希种子buckets    unsafe.Pointer // 指向当前哈希桶的指针oldbuckets unsafe.Pointer // 扩容时指向旧桶的指针nevacuate  uintptr        // 桶进行调整时指示的搬迁进度extra *mapextra // 表征溢出桶的变量
}

map是无序的(原因为无序写入以及扩容导致的元素顺序发生变化),每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
map的长度是不固定的,也就是和slice一样,也是一种引用类型
内置的len函数同样适用于map,返回map拥有的key的数量
map的key可以是所有可比较的类型,如布尔型、整数型、浮点型、复杂型、字符串型……也可以键。下面我们看看如何使用map

var m map[string]int //声明map,指定key和value的数据类型
m = make(map[string]int) //初始化,容量为0
m = make(map[string]int, 200) //初始化,容量为5。强烈建议初始化时给一个合适的容量,减少扩容的概率
m = map[string]int{"语文": 0, "数学": 39} //初始化时直接赋值

9.1添加和删除key值

m["英语"] = 59 //往map里添加key-value对
m ["英语"] = 70 //会覆盖之前的值
delete(m, "数学") //从map里删除key-value对

1.根据key值计算出哈希值
2.取哈希值低位和hmap.B取模确定bucket位置
3.查找该key是否已经存在,如果存在则直接更新值
4.如果没有找到key,则将这一对key-value插入

2.判断某个key值是否存在

if value, exists := m["语文"]; exists {fmt.Println(value)
} else {fmt.Println("map里不存在[语文]这个key")
}

注意如果我们直接使用value去接收那么即使key不存在它也会返回value默认的零值

9.2遍历map

for key, value := range m {fmt.Printf("%s=%d\n", key, value)
}
fmt.Println("-----------")
//多次遍历map返回的顺序是不一样的,但相对顺序是一样的,因为每次随机选择一个开始位置,然后顺序遍历
for key, value := range m {fmt.Printf("%s=%d\n", key, value)
}
fmt.Println("-----------")//一边遍历一边修改
for key, value := range m {m[key] = value + 1
}
for key, value := range m {fmt.Printf("%s=%d\n", key, value)
}
fmt.Println("-----------")//for range取得的是值拷贝
for _, value := range m {value = value + 1
}
for key, value := range m {fmt.Printf("%s=%d\n", key, value)
}

map中的key可以是任意能够用==操作符比较的类型,不能是函数、map、切片,以及包含上述3中类型成员变量的的struct。map的value可以是任意类型。

10.channel

go语言里面的channel比较的复杂在这里我们只是简单的了解一下在后面在深入研究
channel(管道)底层是一个环形队列(先进先出),send(插入)和recv(取走)从同一个位置沿同一个方向顺序执行。sendx表示最后一次插入元素的位置,recvx表示最后一次取走元素的位置。

10.1 管道的使用

var ch chan int //管道的声明
ch = make(chan int, 8) //管道的初始化,环形队列里可容纳8个int
ch <- 1                //往管道里写入(send)数据
ch <- 2
ch <- 3
ch <- 4
ch <- 5
v := <-ch //从管道里取走(recv)数据
fmt.Println(v)
v = <-ch
fmt.Println(v)

10.2定义只读或者只写管道

read_only := make (<-chan int)   //定义只读的channel
write_only := make (chan<- int)   //定义只写的channel

定义只读和只写的channel意义不大,一般用于在参数传递中。

10.3遍历管道

//只能向channel里写数据
func send(c chan<- int) { c <- 1
} //只能取channel中的数据
func recv(c <-chan int) {_ = <-c
}//返回一个只读channel
func (c *Context) Done() <-chan struct{} {return nil
}

可以通过for range的方式遍历管道,遍历前必须先关闭管道,禁止再写入元素。

close(ch) //遍历前必须先关闭管道,禁止再写入元素
//遍历管道里剩下的元素
for ele := range ch {fmt.Println(ele)
}

总结:
slice、map和channel是go语言里的3种引用类型,都可以通过make函数来进行初始化(申请内存分配)。因为它们都包含一个指向底层数据结构的指针,所以称之为“引用”类型。引用类型未初始化时都是nil,可以对它们执行len()函数,返回0。

11.go语言里面的循环

go中时使用for实现循环的,共有三种形式:

除此以外,for循环还可以直接使用range对slice、map、数组以及字符串等进行迭代循环,格式如下:

for key, value := range oldmap {newmap[key] = value
}

11.1 break、goto、continue


break主要用于循环语句跳出循环,和c语言中的使用方式是相同的。且在多重循环的时候还可以使用label标出想要break的循环。
实例代码如下:

func main() {for i:=0;i<10;i++{if i==6{break}}}

continue和break的用法非常的类似在这里就不演示了,下面我们来看看这个goto.
goto语句主要是无条件转移到过程中指定的行。goto语句通常和条件语句配合使用,可用来实现条件转移、构成循环以及跳出循环体等功能。但是并不主张使用goto语句,以免造成程序流程混乱。
示例代码如下:

var a int = 0
LOOP: for a<5 {if a == 2 {a = a+1goto LOOP}fmt.Printf("%d\n", a)a++
}

以上代码中的LOOP就是一个标签,当运行到goto语句的时候,此时执行流就会跳转到LOOP标志的哪一行上。

万字带你入门Go语言(建议收藏)相关推荐

  1. 二十万字带你入门C语言-史上最强C语言教程(汇总篇)

    一.前言 至此,史上最强C语言教程系列已经全部完成,今天是给大家来做一个汇总,笔者目前已经完成了C语言阶段的学习,一直以来感谢大家的陪伴与支持,笔者后续还会继续更新C++.数据结构.Linux.Mys ...

  2. 如何学习自动化测试?一文4从个方面带你入门自动化测试【建议收藏】

    首先目前的话主要可以分为两个方向,要么是基于Java的自动化,要么是基于Python的自动化,很多做培训在培训的时候也是这样去划分,不过这个倒是不重要,归根结底都是为了解决问题的. 本文从4个面向去解 ...

  3. 一文带你入门go语言

    一文带你入门go语言 go/golang是一门google开发的编程语言,其代码简洁易读,天生支持并发,完美契合当今互联网生态. 目前Go语言已经⼴泛应用于人工智能.云计算开发.容器虚拟化.⼤数据开发 ...

  4. Linux运维入门教程(☆建议收藏☆)

    在当下,Linux运维是每个企业都不能缺少的岗位,以服务为中心,以稳定.安全.高效为基本,可以确保公司的互联网业务能够7*24小时为用户提供高质量的服务,在企业或产品交付交付运行当中有着十分重要的地位 ...

  5. ❤️整理2万字带你走进C语言(详细讲解+代码演示+图解)❤️(强烈建议收藏!!!)

    目录 一.什么是C语言? 二.第一个C语言程序 起源 代码 程序分析 程序运行 一个工程中出现两个及以上的main函数 代码 运行结果 分析 三.数据类型 数据各种类型 为什么会有这么多的数据类型? ...

  6. 预训练模型需要知道的知识点总结,万字长文带你深入学习(建议收藏)

    前言:如何在有限数据下训练高效的深度学习模型? 利用深度学习自动学习特征已经逐步取代了人工构建特征和统计方法.但其中一个关键问题是需要大量的数据,否则会因为参数过多过拟合. 以下是我为大家准备的几个精 ...

  7. 3万字干货HTML+CSS入门指南(建议收藏)

    什么是浏览器 浏览器是安装在电脑里面的一个软件, 能够将网页内容呈现给用户查看,并让用户与网页交互的一种软件. 就好比QQ一样都是安装在电脑里面的一个软件, 只不过功能不同而已 常见主流浏览器 浏览器 ...

  8. 我把问烂了的⭐MySQL⭐面试题总结了一下(带答案,万字总结,精心打磨,建议收藏)...

    1. MySQL 索引使用有哪些注意事项呢? 可以从两个维度回答这个问题:索引哪些情况会失效,索引不适合哪些场景 索引哪些情况会失效 查询条件包含or,会导致索引失效. 隐式类型转换,会导致索引失效, ...

  9. 我把面试问烂了的⭐MySQL面试题⭐总结了一下(带答案,万字总结,精心打磨,建议收藏)

最新文章

  1. [可行]setoolkit生成木马软件远程控制实例
  2. PostgreSQL的那点事儿
  3. 关于百度地图api测距显示NaN的解决方案
  4. PMCAFF | 产品经理十宗罪(冷静反思篇)
  5. SpringMVC源码解析与思考
  6. server sql 分组 去重 字符串拼接_SQL必知必会
  7. RabbitMQ学习之集群消息可靠性测试
  8. java 界面框架源码_轻量级java web实践-8(框架源码-6)
  9. 【jQuery学习】—jQuery对象的串联
  10. 华为机试(6.17笔试题解析)
  11. 工业控制系统(ICS)
  12. ubuntu deepin-wine 微信之后打不开
  13. 已知华氏温度f c语言,编程题:已知两种温度的换算公式C=(5/9)(F-32),试编写一个程序输入华氏度F,输出摄氏度。...
  14. 拼多多关键词搜索采集商品数据接口,拼多多分类ID搜索采集商品销量接口,拼多多上货接口
  15. 简单易懂且有趣的pycharm运行小游戏
  16. 【C++】细说C语言头文件的路径
  17. 基于FPGA 的PLC/PAC 硬件运行时的实现
  18. 【非参数统计】2.1广义符号检验(R语言)
  19. 设计模式——享元模式
  20. 电子元器件销售如何从小白到精英

热门文章

  1. 四川省评高级教师考职称计算机,四川省中学教师中、高级职称评审条件
  2. C#中推荐使用Environment.NewLine
  3. 微信公众号排查“该公众号提供的服务出现故障,请稍后再试”问题
  4. Elasticsearch5.0 安装 以及 问题集锦
  5. 从头到尾跑起来一个SpringBoot系统
  6. 甲方和大厂外包,哪个更好?这是最醍醐灌顶的回答
  7. 60分钟搞定JAVA加解密
  8. 无需Root权限新型Android木马SpyNote在多个论坛泄露
  9. 【高等数学】去心邻域与邻域的意义
  10. java的发展(8-17新特性整理)