Go语言基础语法 | 青训营笔记

242 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第1天

变量与常量

变量

变量与常量是组成语句的最基本的单元,在语句中使用前需要定义,定义需要说明变量与常量的类型及初始值(对于变量可以不给赋初始值)。

Go语言中声明变量的几种方式:

  • var identifier type ,没有显示赋值,默认赋值为该类型的默认值,例如数值类型的为0,布尔类型的为false,字符串类型的为""(空字符串),指针等类型的为nil。
  • var identifier type = value,有显示赋值的变量定义,需要变量的值能匹配声明的类型。
  • var identifier = value,如果显示赋值定义变量时,可以省略type,将根据value进行自动推导变量的类型。
  • identifier := value,同时也可以省略var关键字,但需要用 :=替代=

上面是变量定义的语法格式,其中出现的几个概念需要解释一下:

  1. 关键字
    程序语言中关键字或者保留字是有特殊含义的一类标识符,例如var就是Go语言中的一个关键字,用来定义常量的。Go语言共有如下25个关键字:
break default func interface select case defer go   
map struct chan else goto package switch const fallthrough  
if range type continue for import return var
  1. 标识符
    标识符用来命名变量、类型等程序实体。和大多数语言类似,Go语言中标识符使用一个或是多个字母(AZ和az)、数字(0~9)、下划线(_)组成的字符串,但是第一个字符不能是数字。
  2. 数据类型
    Go语言常用的数据类型有以下几类:
  • 数值类型

    • 整型:int、uint、int8、int16、int32、int64、uint8、uint16、uint32、uint64、byte、rune、uintptr
    • 浮点型:float32、float64
    • 复数:complex64、complex128
  • 布尔类型:bool,取值只可以是常量 true 或者 false

  • 字符串类型:string

  • 其他类型:

    • 指针类型:*type
    • 数组类型:[len]type、[...]type
    • 切片类型:[]type
    • Map类型:map[type]type
    • 结构体类型:struct { }
    • 接口类型
    • 函数类型
    • Channel类型

几个需要注意的点:

  • 变量定义可以在一条语句中定义多个变量,类似于Python语言,例如:var num1, num2 int = 2, 4
  • 可以采用因式分解关键字定义多个变量,一般用于定义全局变量,可以将var提取到外面,例如:
var (
    num1 int 
    str1 string
)

常量

常量的定义格式主要有两种:

  • const identifier type = value,例如const num int = 100,与变量定义类似,同样可以同时定义多个常量,例如const num1, num2 int = 100, 200;也可以将const提取到外面,例如:
const (
    num1 int = 100
    num2 int = 200
    num3   //num3=200,如果某行没有显示给出值,其值与前一行相等
)
  • const identifier = value,可以省略type,让编译器根据值来推导变量的类型,例如const num = 100

iota是一个特殊的常量,是一个编译器可以修改的常量,在const关键字出现时被赋值为0,外后每一行加1,例如:

const (
    a = iota      //a = 0
    b             //c = 1
    c             //c = 2
    d = 2<<iota   //d = 2 << 3 = 16
    e             //e = 2 << 4 = 32
)

表达式

表达式由变量、常量通过运算符连接而成,Go语言中运算符有以下几类:

  • 算术运算符:+、-、*、/、%、++、--
  • 关系运算符:==、!=、>、>=、<、<=
  • 逻辑运算符:&&、||、!
  • 位运算符:&、|、^、<<、>>
  • 赋值运算符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=
  • 其他运算符:&(取地址)、*(解引用)

程序结构

程序的三大基本结构为:顺序、条件、循环,顺序结构没什么好说的,就是按照语句一行一行执行,下面重点介绍条件及循环结构。

条件

Go语言中有下面几类条件语句:

  1. if...else...语句:与其他编程语言不同的是,if语句不仅可以跟一个逻辑表达式,例如:if a > 0 { },还可以采用两段式,前面获取值,后面根据值来逻辑判断,例如:if a = get(); a > 0 { }
  2. switch语句:switch分支匹配到则break,不需要显示添加,也不会走到下一个匹配条件。
  3. select语句:为

Go语言中switch有三种用法:

  • 根据变量的取值来判断:
switch *identifier* {
case *val1*:
...
case *val2*:
...
case *val3*,*val4*:
...
default:
...
}
  • 根据逻辑表达式的结果来判断:
switch {
case *bool_exp1*:
...
case *bool_exp2*:
...
case *bool_exp3*,*bool_exp4*:
...
default:
...
}
  • type-switch用法:
switch *identifier*.(type) {
case *type1*:
...
case *type2*:
...
case *type3*,*type4*:
...
default:
...
}

此外,case条件从上到下依次匹配,匹配到某个条件之后则break,如果需要显示地还往下进行匹配,则用到一个关键字fallthrough

循环

Go语言循环语句只支持for关键字,主要有以下几种类型:

  1. for init; condition; post { },根据初循环始化值、循环控制条件、每轮循环变化来控制循环。
  2. for condition { },根据表达式条件来控制循环。
  3. for { },无限循环。
  4. for index, value := range identifier { },支持range来遍历的数据结构有字符串、数组、切片等。

在循环过程中可以控制循环的过程,有以下几种语句:

  1. break:终止整个循环
  2. continue:终止本轮循环,开始下一轮的执行
  3. goto LABEL:跳转到定义的标签处,可以在循环前、循环后或者循环中。

函数

函数的定义

由语句和基本的控制就可以写出一个基本的程序了,但如果将公共的代码抽象提取出来供其他地方使用,这就要用到函数。Go语言中函数的定义如下:

func func_name ( params_list ) return_types {
 //function body
}

其中:

  • func为函数定义的关键字
  • func_name为函数名
  • params_list为函数的参数列表,每个参数格式为param_name param_type,如果多个参数类型相同,前面的参数类型可以省略,如(x int, y int)可以简写为(x, y int),Go语言函数参数传递都是传值的,如果函数参数为指针,则在函数内部对该指针内容的修改在函数结束之后也是生效的,本质上还是传值。
  • return_types为函数的返回值类型,没有返回值时为空,可以有一个或多个返回值。

struct的方法

如果需要定义struct类型的方法时,需要在函数名前增加struct类型的参数,等价于函数参数列表前显示增加一个struct类型的参数,类似于C++/Java中的this参数,有两种定义方法:

  • func (struct_name struct_type_name) func_name ( params_list ) return_types,等价于 func func_name ( struct_name struct_type_name, params_list ) return_types
  • func (struct_name *struct_type_name) func_name ( params_list ) return_types,等价于 func func_name ( struct_name *struct_type_name, params_list ) return_types

相对于来说第二种传结构体指针使用更多,这种方法避免了结构体内容的大量拷贝,同时可以在函数内部对结构体实例进行修改生效。

defer

defer是Go语言中的关键字,只能出现在函数调用语句前,如果用defer描述的函数调用,该语句只在函数结束前进行调用,和该函数调用语句出现的位置没有关系。主要可以用来在函数结束前进行清理动作,例如关闭文件描述符、释放锁等。如果有多个defer语句,函数结束前逆序执行这些defer描述的函数。

Go语言中源代码以包的形式组织,如果要将包中的函数提供给其他包来调用,约束是函数名首字母要大写,否则为一个包内的私有函数。关于包名还有以下几点需要注意的:

  • 文件名与包名没有直接关系,不一定要将文件名与包名定成同一个。
  • 文件夹名与包名没有直接关系,并非需要一致。
  • 同一个文件夹下的多个文件只能有一个包名,否则编译报错。

要导入一个包格式为 import [package_abbr] "package_path",其中package_path为包的相对目录路径,Go编译时会在GOPATH环境变量定义的目录下src搜索该相对目录;如果包名太长,可以简写。

如果导入多个包,除了一行行import之外,还可以类似变量定义合并同类项将import提取出来,例如:

import (
    math
    fmt
)