Hello world

  1. package main // 包名与文件名没有关系。main 函数所在包名必须是 main
  2. import ( // 导包
  3. "fmt"
  4. )
  5. // main 函数,程序入口
  6. func main() { // 左大括号必须和函数名在同一行
  7. fmt.Println("Hello, golang!") // 分号可加可不加,和 JS 一样,一般不加
  8. fmt.Printf("Hello, %s!", "golang") // Printf 格式化输出
  9. }

Go 程序的入口是main函数,包含main函数的包名必须是main

Go 中很多代码规范是在编译器层面限定死的,比如“函数左大括号必须和函数名在同一行”,否则编译都无法通过。

变量

四种声明方式

  1. // 方式一:声明一个变量,不赋值,会有默认值
  2. var a int
  3. fmt.Println("a = ", a) // 字符串用双引号,单个字符用单引号
  4. fmt.Printf("type of a: %T\n", a) // 打印数据类型用 %T
  5. // 方式二:声明一个变量,初始化一个值
  6. var b float64 = 10 // go 没有 double,float64 就相当于 double
  7. fmt.Println("b = ", b)
  8. fmt.Printf("type of b: %T\n", b)
  9. // 方式三:初始化时省略数据类型,编译器会根据初始化值自动判断类型
  10. var c = 100.0
  11. fmt.Println("c = ", c)
  12. fmt.Printf("type of c: %T\n", c)
  13. // 方式四:省略 var 关键词和数据类型(最常用)
  14. d := "go"
  15. fmt.Println("d = ", d)
  16. fmt.Printf("type of d: %T\n", d)

:::warning 注:声明全局变量时不能使用方式四。 :::

不能重复声明

  1. var c = 100.0
  2. //var c = 10.0 // 不可重复声明
  3. d := "go"
  4. //d := "golang" // 这种方式同样不能重复声明变量

声明多个变量

  1. var e, f int = 1, 2
  2. var g, h = 3, "4"
  3. var (
  4. i int = 5
  5. j = "6"
  6. )
  7. fmt.Println(e, f, g, h, i, j)

注意

  1. 变量类型不可修改。

  2. Go 中局部变量声明后必须要使用,否则编译会报错。全局变量只声明不使用不会报错,但 IDE 一般也会提醒。

匿名变量

_在 Go 中表示匿名变量(和 Python 相同),但在 Python 中只是一个约定,而在 Go 中变成了语法。
Python 中匿名变量可以当普通变量使用,而在 Go 中不行,比如 fmt.Println(_)会报错。

匿名变量的作用主要是接收不需要用的数据。比如下面的例子:

  1. a, b := func() // 这个函数返回两个值

假如a的值后面的程序会用到,而b用不到。其他语言这样写无所谓,但 Go 不行,因为 Go 的变量定义后必须要使用,此时就可以使用匿名变量:

  1. a, _ := func()

常量

定义常量

  1. const a int = 10 // 定义常量
  2. const ( // 定义多个常量,可以分别初始化
  3. b = 1
  4. c = 2
  5. d = 3
  6. )
  7. const ( // 若没有指定初始化的值,会复用 *上一行* 的 *表达式*
  8. e = 1
  9. f // 1
  10. g = 2
  11. h // 2
  12. )

定义枚举类型

枚举类型本质上就是个常量组,不过我们只关注常量的名字,而不关注其值。

  1. const ( // 定义枚举类型
  2. A = iota // 初始化为 iota,每行的 iota 自增 1,第一行的 iota 默认为 0
  3. B
  4. C
  5. )
  6. const (
  7. D = iota * 10 // D = 0 * 10 = 0
  8. E // E = 1 * 10 = 10
  9. F // F = 2 * 10 = 20
  10. )
  11. const (
  12. AA, AB = iota, iota * 2 // 0, 0
  13. BA, BB // 1, 2
  14. CA, CB // 2, 4
  15. DA, DB = iota, iota * 3 // 公式变了,但 iota 仍然累加。所以:3, 9
  16. )
  17. const (
  18. G = 2
  19. H = iota // 1。iota 可以认为是行数(从 0 开始的)
  20. )

:::warning iota只能在const ()中使用。 :::

基本数据类型

布尔

  1. var _ bool // 1 byte

整数

  1. var _ int8 // 1 byte, -128 ~ 127
  2. var _ int16
  3. var _ int32
  4. var _ int64
  5. var _ int // 在 32 位操作系统上相当于 int32,在 64 位操作系统上相当于 int64
  6. var _ uint8 // 1 byte, 0 ~ 257
  7. var _ uint16
  8. var _ uint32
  9. var _ uint64
  10. var _ uint // 在 32 位操作系统上相当于 uint32,在 64 位操作系统上相当于 uint64
  11. var _ = 0 // 不指定类型,默认为 int
  12. fmt.Println("int64 能表示的最大值 =", math.MaxInt64, "; 能表示的最小值 =", math.MinInt64)

浮点数

  1. var _ float32
  2. var _ float64
  3. fmt.Println("float32 能表示的最大值 =", math.MaxFloat32)
  4. var flt = 0.0 // 默认为 float64,暂不知道是否与操作系统位数有关

byte 和 rune

byte其实是uint8的别称。专门再定义一个byte是为了表示强调,因为uint8比较特殊,刚好等于一个字节的大小。
下面是源码中byte的定义:

  1. // byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
  2. // used, by convention, to distinguish byte values from 8-bit unsigned
  3. // integer values.
  4. type byte = uint8

runeint32的别称。常用来处理中文字符。
下面是源码中rune的定义:

  1. // rune is an alias for int32 and is equivalent to int32 in all ways. It is
  2. // used, by convention, to distinguish character values from integer values.
  3. type rune = int32

字符

Go 中字符和字符串有区别,单引号表示字符,双引号表示字符串。

  1. var c = 'c'
  2. fmt.Printf("%T\n", c) // 结果:int32。所以说 Go 中字符实际上是 int32 类型,没有专门的 char 类型
  3. fmt.Println(c) // 结果:99。直接打印会打印出 ascii 码值
  4. fmt.Printf("%c\n", c) // 结果:c。这样才能打印出字符形式

复数

TODO

类型转换

基本类型的隐式类型转换

:::info 静态类型:变量定义后无法改变类型。
强类型:不支持隐式类型转换。 :::

Go 语言是强类型、静态类型语言。
Python 是强类型、动态类型语言。
C/C++ 是弱类型、静态类型语言。

Go 语言不支持 变量间 的隐式类型转换:

  1. var a int = 1
  2. var b float64
  3. b = a // 报错。不支持(C/C++ 中这种情况是支持的)
  4. var c int16 = 1
  5. var d int32
  6. d = c // 报错。连两种整型之间的转换都不支持

常量转变量的隐式类型转换是简单支持的:

  1. var e int = 5.0 // 简单浮点型 隐式转换为 整型
  2. var f int = 5.1 // 报错。这种是不支持的
  3. var g float32 = 5 // 整型 隐式转换为 浮点型
  4. var h bool = 1 // 报错。不支持(C/C++ 中是支持的)
  5. const A int = 1
  6. var i float32 = A // 当然这种常量转变量也是不支持的

基本类型的强制类型转换

Go 允许底层结构相似的两个类型强制转换。

跟 C/C++ 类似,支持浮点型和整型互转,支持字符转换为整型、浮点型(因为字符底层实际上是int32),不支持字符串和其他类型之间的转换。
与 C/C++ 不同的是,不支持布尔型和整型、浮点型之间的转换。

低精度转高精度是安全的,高精度转低精度会丢失精度。例如int->float64int64->int32会丢失精度,这个很好理解。

  1. var a2 int32 = 1
  2. var b2 int64 = int64(a2)
  3. var c2 float64 = float64(b2)
  4. var e2 string = "2"
  5. var f2 int = int(e2) // 报错。不支持
  6. var g2 bool = bool(1) // 报错。不支持
  7. var h2 int = int(g2) // 报错。不支持
  8. type myInt int // 自定义一种类型,myInt 相当于 int 的别名
  9. var i2 myInt = 1
  10. var j2 int = i2 // 报错。即使是别名,也不支持隐式类型转换
  11. var j2 int = int(i2) // 但由于底层结构相同,所以支持强制类型转换
  12. var k2 string = "hello"
  13. var l2 []rune = []rune(k2) // 字符串和切片的底层结构是相似的,所以可以强制转换

字符串与基本类型之间的转换



Go 语言 - 字符串

运算符

  1. 算数运算符、关系运算符、逻辑运算符、位运算符、赋值运算符等都和 C/C++ 差不多,只是不支持++a--a的写法(但支持a++a--)。

    1. a := 1
    2. a++
    3. a--
    4. ++a // 不支持
    5. --a // 不支持
    6. fmt.Println(a++) // 不能和别的语句嵌套。其实这样确实更符合逻辑,因为 a++ 实际上是一条语句而非一个表达式
  2. &取地址、*取内容 也和 C/C++ 相同。

  3. 运算符优先级也和 C/C++ 差不多。

:::warning 运算符这一方面细节很多很多,没必要全搞清楚,平时用到的时候再查或者自己试试就知道了。 :::

控制台输入输出

输出:fmt.Printf()fmt.Println()
指定格式的方式部分与 C 的printf相似,具体看老师笔记。

输入:fmt.Scanf()fmt.Scanln()

  1. var name string
  2. var age int
  3. _, _ = fmt.Scanf("%s, %d", &name, &age) // 跟 C 的 scanf 基本相同
  4. _, _ = fmt.Scanln(&name, &age) // 相当于 fmt.Scanf("%s %d", &name, &age)

流程控制

if语句

基本写法:

  1. num := 10
  2. if num < 0 {
  3. // some code
  4. } else if num == 0 {
  5. // some code
  6. } else {
  7. // some code
  8. }

简化写法:

  1. if n := 10; n < 0 {
  2. // some code
  3. } else if n == 0 {
  4. // some code
  5. } else {
  6. // some code
  7. }

注意:这种写法中,n的作用域仅仅是if语句块,不能在if语句块外使用。

for循环

Go 中没有whiledo while,只有for
for的用法很多,功能很强大。

用法一(与 C/C++ 相似):

  1. sum := 0
  2. for i := 0; i < 10; i++ {
  3. sum += i
  4. }

用法二(相当于while):

  1. sum := 0
  2. i := 0
  3. for i < 10 {
  4. sum += i
  5. i++
  6. }

用法三(相当于for(;;),即无限循环):

  1. sum := 0
  2. i = 0
  3. for {
  4. if i >= 10 {
  5. break
  6. }
  7. sum += i
  8. i++
  9. }

用法四(for-range,专门用来遍历字符串、数组、切片、map 等):

  1. for index, char := range "hello,世界" { // 遍历字符串
  2. fmt.Printf("index = %d, char = %c\n", index, char)
  3. }

:::info 注:使用这种方式遍历字符串时,是可以自动处理中文的。 :::

breakcontinue

不用说了,都一样。

goto语句

Go 中是有goto语句的,用法和 C/C++ 一样。

goto语句能不用则不用,因为会影响程序结构,导致代码可读性差,难以维护。

goto语句常见的两个使用场景:

  1. 跳出多重循环。
  2. 集中处理错误。

switch语句

有些情况下使用switch代替if可以增强代码可读性。

由于 Go 中的switch做了扩展,所以在 Go 中使用switch比在 C/C++ 中更频繁。
做接口类型判断时经常用到switch

这种用法和 C/C++ 相似:

  1. sex := 0
  2. switch sex {
  3. case 0: fmt.Println('女') // 只有一条语句的话可以写在一行里
  4. case 1: // 多条语句可以这样写
  5. fmt.Println('男')
  6. //fallthrough // Go 的 switch 中默认不用在每个 case 最后写 break。如果要用到 C/C++ 中不写 break 时的那种继续执行下一个 case 的效果,可以用 fallthrough 关键字
  7. default: { // 可以加大括号
  8. fmt.Println("未知")
  9. }
  10. }

这个写法与 C/C++ 不一样:

  1. sex := 0
  2. switch sex {
  3. case 0, 1:
  4. fmt.Println("已知")
  5. default:
  6. fmt.Println("未知")
  7. }

这是 C/C++ 中没有的扩展写法:

  1. score := 90
  2. switch {
  3. case score >= 90: // case 不再只能是一个值,而可以是一个逻辑表达式
  4. fmt.Println('A')
  5. case 80 <= score && score < 90:
  6. fmt.Println('B')
  7. case 70 <= score && score < 80:
  8. fmt.Println('C')
  9. case 60 <= score && score < 70:
  10. fmt.Println('D')
  11. default:
  12. fmt.Println('E')
  13. }