添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
帅气的枕头  ·  python git库安装 - CSDN文库·  3 月前    · 
好帅的土豆  ·  typescript+webpack+api ...·  1 年前    · 

主要记录下 6.824 课程中关于 Go 的学习教程 Online Go tutorial 的内容,同时也作为笔者学习 Go 的笔记。Go 安装可以参考官方教程 Installing Go

基本代码规范

这里主要列举下和传统的 C/C++ 规范不一致的地方:

1.语句的末尾不需要使用 ';' 作为语句的结束标记( 编译时会自动添加,添加规则可以参考 Effective Go - Semicolons );

2.对于函数的定义而言,函数体开始的左括号应与对应函数名在同一行,不允许换行( 这一点实际和第一点有一定关系,若进行换行的话,函数名所在行会被自动加入 ';',从而出现错误);

3.在条件语句,循环语句开始位置可以插入一个初始化语句,该初始化语句在条件/循环判断之前执行,并仅可在条件/循环语句内可见;

4.if / for 等语句的条件部分不需要使用 "()" 将条件包围,同时不支持 C/C++ 中使用单条语句从而省略 "{}" 的写法,即执行体必须位于 "{}" 内;

index

1. 变量

2. 控制语句

3. 复合类型

3.1 struct

3.2 array

3.3 slice

3.4 map

使用关键字 var 定义一个或多个变量,使用形如 var x int 的 var + 标识符 + 类型 的方式进行变量定义,多个变量同类型时可以在最后进行统一的类型声明。

    var x int       //定义整形变量 x
    var x, y int    //定义整形变量 x, y

变量在定义的同时可以进行初始化,当变量定义指定了初始值时,变量的类型被定义为初始值的类型,此时不再需要进行变量类型的声明。

Go 同时提供了变量定义以及赋值的语法糖,即使用 ':=' 符号, 该符号仅可在函数体内部使用,无法在全局作用域使用 ,表示定义一个变量并对其进行赋值,此时的变量定义不再需要使用 var 关键字。

    k := 3        //函数体内部定义整形变量 k,初始值为 3

Go 中可使用的基本数据类型如下所示,这里直接参考的 A Tour of Go 的记录。对应的数据类型在未被初始化时,初始值为 0(整形),false(bool型),""(字符串类型)以及 nil(指针/slice/map类型)。在 Go 中指针类型不支持算数运算,其他操作与 C 中基本一致。

string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // 与 uint8 相同 rune // 与 int32 相同,represents a Unicode code point? float32 float64 complex64 complex128 // 复数类型 *T // 指针类型

多个不同类型的变量的定义可以通过 var() 的形式进行联合定义。

x int     y bool = false      z float32 = 3.14 // 使用 var() 联合定义多个变量

在 Go 中,不同类型的变量需要使用强制类型转换进行类型转换(貌似常量是不能强制类型转换的)。 Go 中强制类型转换的操作为 type() 的形式。下列示例表示从 double 到 int 类型的转换

    var x float32 = 3.14
    var y int = int(x)            // double 类型强制类型转换为 int

Go 中的常量使用 const 关键字定义,常量需要在定义时给出初始值,后续无法修改,故而定义时不需要额外说明类型,同时常量定义无法使用 ':=' 操作符定义。

    const a, b = "a", false        //定义常量 a 和 b

Go 中仅存在 for 循环语句,其控制语句中包含有 "初始化;控制条件;循环后操作" 三个部分。上述这三个部分均为可选项。若想在初始化部分定义变量,则必须使用 ":=" 操作符,且定义的变量仅在 for 循环内部可见。与其他语言不同的是 Go 的循环语句不需要使用 "()" 包括 for 的控制语句,同时循环语句块必须位于 "{}" 中。

    for i := 1; i < 10; i++{
      //statements  

  在 Go 中也可使用类似 C 中 while 语句的写法,达到 while 语句的目的,此时控制语句中仅包含有控制条件判断语句。甚至 Go 可以完全省略 for 循环语句中的三个部分,此时为一个无限循环。

    i := 1
    for i < 100 {    // Go 中的 while 语句
        // statements  
    for{          // Go 中的无限循环
        // statements  

  使用 if 作为条件语句关键字,Go 中的 if 语句同样不需要使用 "()" 包围判断条件,但是需要使用 "{}" 来包围函数体。另外 Go 中的 if 语句支持在判断条件之前加入独立的初始化语句,该初始化部分在 if 判断执行之前执行,并仅在 if 语句内部可见(与 if 配对的 else 关键字对应的函数体中也可见)。

    i := 3
    if i < 10 {    // 判断语句
      // statements  
    if i :=3; i < 10 {    // 条件语句前加入初始化语句
      // statements  
    } else {                // if 语句加入 else 时,else 关键字需与 "}" 同一行
      // statements  

  switch

  Go 中的 switch 语句与 if 语句一样,可以在进行匹配的语句前插入一个初始化语句。与 C/C++ 所不同的是,Go 中只会执行匹配的 case 条件对应的语句,而不会执行后续相邻的 case 条件的语句(相当于每个 case 语句后默认存在 break )。

    switch i:=3; i {  // 可插入一个初始化语句
      case 1:
            //statements
      case 2:
            //statements
      default:
            //statements

   同时 switch 语句的 case 条件并不需要是常量,甚至可以是函数。

     switch x{
      case 0:
      case f():  // x != 0 时,函数 f 会被调用...写法略浮夸  

  switch 甚至可以忽略比较对象,实现类似 if-else 的逻辑。

    switch x:=3;{    // 初始化语句可省略
      case x < 5:
      case x < 10:
      default:  

  struct 结构体

  struct 结构体为一个数据元素的集合,通过 type 关键字定义,通过 "." 来进行访问( 在 Go 中允许使用结构体变量或者变量的指针直接通过 "." 进行结构体元素访问)。

    type test struct{
      x int
      y int

  对于定义的 struct 类型的变量,其结构体成员的初始值为对应类型的初始值,如 int 类型初始值为 0。用户也可以在定义时进行初始化。

    a := test{}        // 定义 struct 变量 a,其成员元素均为 0
    b := test{1, 2}    // 定义 struct 变量 b,其中 b.x = 1, b.y = 2
    c := test{ x:1}    // 定义 struct 变量 c,其中 c.x = 1, c.y = 0

  Go 中数组类型为 [n]T,其中 n 为数组中的元素个数,T 为数组元素的类型,n 为数组类型的一部分,数组的大小后续不能修改。

    var arr [3]int               // 3 个元素的整形数组
    arr2 := [3]int{ 0, 1, 2 }    // 另一种定义方式

  slice

  Go 中提供 slice 类型,用于访问数组中的元素。slice 类型为 []T,T 为其访问的元素的类型。slice 类型变量通过 arr[ low : high ] 的方式定义,其中 arr 为对应的数组,low 和 high 分别表示数组中索引,slice 类型可以访问数组中 [ low : high ) 范围的变量。当 low 被省略时,默认视为 0,high 被省略时,默认为数组长度。slice 类型可以看作对于数组中元素的引用,通过 slice 对元素的修改会对应修改数组中对应的元素。

    // 数组
    var arr [5]int = { 1, 2, 3, 4, 5 }
    //slice
    var a []int = arr[1:3]       // a[0]=2, a[1]=3 两个元素
    a[0:5]                 // 均可索引整个数组
    b := []int{ 1, 2, 3 }        // 定义数组,并建立了对应的 slice b

  slice 类型变量包含有长度(length)和容量(capacity)两个信息,其中 length 表示 slice 类型可以访问的元素的个数,capacity 表示 slice 对应的数组从 slice 开始位置到数组结束位置的长度。可通过 len(a) 和 cap(a) 的方式查看 slice 的这两个属性。slice 未被初始化时,其默认值为 nil,length 与 capacity 属性均为 0,没有数组与之对应。

    a := []int{ 1, 2, 3, 4, 5, 6 }
    a[    : 0 ]        // len = 0, cap = 6
    a[    : 4 ]        // len = 4, cap = 6
    a[ 2 :    ]        // len = 2, cap = 4

   slice 变量还可以通过 Go 中内置的 make 函数进行创建,make 函数的使用形式为 make( T, args ) 。其中 T 为构建的类型,后接构建的参数。

    a := make( []int, 4 )       // 构建一个长度为 4 的数组的 slice,数组元素默认为 0
    b := make( []int, 3, 5 )    // 构建一个长度为 5 的数组的 slice,索引的slice 的 length 为 3

  slice 变量可以通过内置的 append 函数进行扩充。 append 函数签名为 func append( s []T, vs ... T ) []T,其中第一个参数为进行 append 操作的 slice 变量,后面为具体进行 append 的元素。append 支持逐元素的添加,也支持添加另一个 slice.当进行 append 操作的 slice 所对应的底层数组 capacity 足够时,append 操作对应会修改底层数组的值,若 capacity 不足时,append 操作会进行数组的重新分配,并返回新的 slice 结果。

    v := []int{}
    v := append( v, 1 )                // 添加一个元素
    v := append( v, []int{1,2,3}... )    // 添加另一个 slice 中的元素,此时 append 的 slice 后需要加上 '...'

   在 for 循环中,可以使用 range 关键字来简化对 slice 元素的遍历操作,每次循环过程中,range 会返回两个值,第一个为元素对应 slice 中的 index,第二个为 slice 在该 index 位置的值的拷贝。若不需要使用 range 返回的值的内容,可以使用 "_" 来忽略。同时,在 range 用法中,若只需要使用 slice 的索引,则可以省略第二个参数,直接使用 i := range a 即可。

    a := []int{ 1, 2, 3, 4, 5 }
    for index, _ := range a {
      // statements  
    for i := range a {    // 若只是用索引,则可以直接省略第二个参数
        // statements

  更多关于 slice 介绍可以参考 Go Slices: usage and internals 。

  Go 中提供 map 来建立 key 与 value 之间的关系。map 变量的默认值为 nil,无法进行任何操作。用户可以通过赋值或 make 函数获取可操作的 map。

    var m map[int]int           // m 为 nil,无法进行操作 
    m := map[int]int{}          // m 为空的 map,可以进行操作 
    m := make( map[int]int )
    m := make[int]int{ 1:2, 3:4 }  // m 为一个 int 到 int 的 map

  map 可支持的操作如下所示:

    a := map[int]int{}           // 获取一个空的 map 变量 a
    a[ 1 ] = 3                    // 添加/修改 key / value 关系
    b = a[ 1 ]                    // 返回 key 对应的 value 值,若对应的 key 不存在,直接返回对应类型的默认零值(注意不存在对应元素时不会自动创建,与 C++ map 不同)
    a[1] ++                       // 更新 key = 1 所对应的 value 值,若 key = 1 不存在,则会创建一个值为 0 的映射并继续操作 
    delete( a, 1 )                // 删除 map 中的 key
    value, ok := a[3]           // 测试 map 中是否含有对应的 key,若存在,则 ok 为 true,否则为 false,对应的 value 中包含有 key 对应的 value 或对应类型的初始零值