Mr.Go


  • 每个 Go 程序都由包构成
  • 程序都从 main 包开始运行
  • 下面的程序通过导入路径 "fmt""math/rand" 来使用这两个包
  • 按照约定,包名与导入路径的最后一个元素一致,例如,"math/rand" 包中的源码均以 package rand 语句开始。就像 mian 包以 package main 开始
    package main
    
    import (
    	"fmt"
    	"math/rand"
    )
    
    func main() {
    	fmt.Println("我最喜欢的数字是 ", rand.Intn(10))
    }

导入

  • 此代码用圆括号将导入的包分成一组,这是“分组”形式的导入语句。
  • 当然你也可以编写多个导入语句,例如:
    import "fmt"
    import "math"
  • 不过使用分组导入语句要更好。
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Printf("现在你有了 %g 个问题.", math.Sqrt(7))
}

导出名

  • Go 中,如果一个名字以大写字母开头,那么它就是已导出的。例如,Pizza 就是个已导出名,Pi 也同样,它导出自 math 包。
  • pizzapi 并未以大写字母开头,所以它们是未导出的。
  • 在导入一个包时,你只能引用其中已导出的名字。 任何「未导出」的名字在该包外均无法访问。
  • 执行代码,观察错误信息。
    ./prog.go:9:19: undefined: math.pi
  • 要修复错误,请将 math.pi 改名为 math.Pi,然后再试着执行一次。
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.pi)
}

函数

  • 函数可接受零个或多个参数。
  • 在本例中,add 接受两个 int 类型的参数。
  • 注意类型在变量名的后面。
    package main
    
    import "fmt"
    
    func add(x int, y int) int {
    	return x + y
    }
    
    func main() {
    	fmt.Println(add(42, 13))
    }
    

函数形参类型简写

  • 当连续两个或多个函数的已命名形参类型相同时,除最后一个类型以外,其它都可以省略。
  • 在上例中,x int, y int,可以被简写为 x, y int
    package main
    
    import "fmt"
    
    func add(x, y int) int {
    	return x + y
    }
    
    func main() {
    	fmt.Println(add(42, 13))
    }

多返回值

  • 函数可以返回任意数量的返回值。
  • swap 函数返回了两个字符串。
    package main
    
    import "fmt"
    
    func swap(x, y string) (string, string) {
    	return y, x
    }
    
    func main() {
    	a, b := swap("hello", "world")
    	fmt.Println(a, b)
    }

带名字的返回值

  • Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。
  • 返回值的命名应当能反应其含义,它可以作为文档使用。
  • 没有参数的 return 语句会直接返回已命名的返回值,也就是「裸」返回值。
  • 裸返回语句应当仅用在下面这样的短函数中。在长的函数中它们会影响代码的可读性。
  • 命名返回值确定返回值为 int 类型
    package main
    
    import "fmt"
    
    func split(sum int) (x, y int) {
    	x = sum * 4 / 9
    	y = sum - x
    	return
    }
    
    func main() {
    	fmt.Println(split(17))
    }

变量

  • var 语句用于声明一系列变量。和函数的参数列表一样,类型在最后。
  • 如例中所示,var 语句可以出现在包或函数的层级。
    package main
    
    import "fmt"
    
    var c, python, java bool
    
    func main() {
    	var i int
    	fmt.Println(i, c, python, java)
    }

变量的初始化

  • 变量声明可以包含初始值,每个变量对应一个。
  • 如果提供了初始值,则类型可以省略;变量会从初始值中推断出类型。
package main

import "fmt"

var i, j int = 1, 2

func main() {
	var c, python, java = true, false, "no!"
	fmt.Println(i, j, c, python, java)
}

短变量声明

  • 在函数中,短赋值语句 := 可在隐式确定类型的 var 声明中使用。
  • 函数外的每个语句都 必须 以关键字开始(varfunc 等),因此 := 结构不能在函数外使用。
    package main
    
    import "fmt"
    
    func main() {
    	var i, j int = 1, 2
    	k := 3
    	c, python, java := true, false, "no!"
    
    	fmt.Println(i, j, k, c, python, java)
    }

基本类型

  • Go 的基本类型有
    bool
    string
    int  int8  int16  int32  int64
    uint uint8 uint16 uint32 uint64 uintptr
    byte // uint8 的别名
    rune // int32 的别名
         // 表示一个 Unicode 码位
    float32 float64
    complex64 complex128
  • 本例展示了几种类型的变量。 和导入语句一样,变量声明也可以「分组」成一个代码块。
  • intuintuintptr 类型在 32-位系统上通常为 32-位宽,在 64-位系统上则为 64-位宽。当你需要一个整数值时应使用 int 类型, 除非你有特殊的理由使用固定大小或无符号的整数类型。
    package main
    
    import (
    	"fmt"
    	"math/cmplx"
    )
    
    var (
    	ToBe   bool       = false
    	MaxInt uint64     = 1<<64 - 1
    	z      complex128 = cmplx.Sqrt(-5 + 12i)
    )
    
    func main() {
    	fmt.Printf("类型:%T 值:%v\n", ToBe, ToBe)
    	fmt.Printf("类型:%T 值:%v\n", MaxInt, MaxInt)
    	fmt.Printf("类型:%T 值:%v\n", z, z)
    }

零值

  • 没有明确初始化的变量声明会被赋予对应类型的 零值。
  • 零值是:
    • 数值类型为 0,
    • 布尔类型为 false,
    • 字符串为 “”(空字符串)。
      package main
      
      import "fmt"
      
      func main() {
      	var i int
      	var f float64
      	var b bool
      	var s string
      	fmt.Printf("%v %v %v %q\n", i, f, b, s)
      }

类型转换

  • 表达式 T(v) 将值 v 转换为类型 T
  • 一些数值类型的转换:
    var i int = 42
    var f float64 = float64(i)
    var u uint = uint(f)
    或者,更加简短的形式:
    i := 42
    f := float64(i)
    u := uint(f)
  • C 不同的是,Go 在不同类型的项之间赋值时需要显式转换。试着移除例子中的 float64 或 uint 的类型转换,看看会发生什么。
    package main
    
    import (
    	"fmt"
    	"math"
    )
    
    func main() {
    	var x, y int = 3, 4
    	var f float64 = math.Sqrt(float64(x*x + y*y))
    	var z uint = uint(f)
    	fmt.Println(x, y, z)
    }

类型推断

  • 在声明一个变量而不指定其类型时(即使用不带类型的 := 语法 var = 表达式语法),变量的类型会通过右值推断出来。
  • 当声明的右值确定了类型时,新变量的类型与其相同:
    var i int
    j := i // j 也是一个 int
  • 不过当右边包含未指明类型的数值常量时,新变量的类型就可能是 intfloat64complex128 了,这取决于常量的精度:
    i := 42           // int
    f := 3.142        // float64
    g := 0.867 + 0.5i // complex128
  • 试着修改示例代码中 v 的初始值,并观察它是如何影响类型的。
    package main
    
    import "fmt"
    
    func main() {
    	v := 42 // 修改这里看看!
    	fmt.Printf("v is of type %T\n", v)
    }

常量

  • 常量的声明与变量类似,只不过使用 const 关键字。
  • 常量可以是字符、字符串、布尔值或数值。
  • 常量不能用 := 语法声明。
    package main
    
    import "fmt"
    
    const Pi = 3.14
    
    func main() {
    	const World = "世界"
    	fmt.Println("Hello", World)
    	fmt.Println("Happy", Pi, "Day")
    
    	const Truth = true
    	fmt.Println("Go rules?", Truth)
    }

数值常量

  • 数值常量是高精度的值。
  • 一个未指定类型的常量由上下文来决定其类型。
  • 再试着一下输出 needInt(Big) 吧。
  • int 类型可以存储最大 64 位的整数,根据平台不同有时会更小。)
    package main
    
    import "fmt"
    
    const (
    	// 将 1 左移 100 位来创建一个非常大的数字
    	// 即这个数的二进制是 1 后面跟着 100 个 0
    	Big = 1 << 100
    	// 再往右移 99 位,即 Small = 1 << 1,或者说 Small = 2
    	Small = Big >> 99
    )
    
    func needInt(x int) int { return x*10 + 1 }
    func needFloat(x float64) float64 {
    	return x * 0.1
    }
    
    func main() {
    	fmt.Println(needInt(Small))
    	fmt.Println(needFloat(Small))
    	fmt.Println(needFloat(Big))
    }

  目录