[关闭]
@mrbourne 2019-07-05T07:54:51.000000Z 字数 4426 阅读 725

GO基础学习笔记

未分类


第一部分 语言

第一章 类型

1.1 变量

Go是静态类型的语言,不能在运行期间改变变量类型。

使用var定义变量,自动初始化为零值。若提供初始值,可以省略变量类型,由编译器自动判断。

  1. var x int
  2. var f float = 1.5
  3. var s = "abc"

在函数的内部,可用更简略的“:=”方式定义变量

  1. func main(){
  2. x := 123 //这里要注意是定义新的局部变量,还是修改全局变量。该方式容易出错。
  3. }

可以一次定义多个变量

  1. var x, y, z int
  2. var s, n = "abc", 123
  3. var (
  4. a int
  5. b float
  6. )
  7. func main(){
  8. n, s := 0x1234, "Hello, World!"
  9. println(x, s, n)
  10. }

多变量赋值时,先计算所有相关值,再从左向右一次赋值。

  1. data, i := [3]int{0, 1, 2}, 0
  2. i, data[i] = 2, 100 // (i = 0) -> (i = 2), (data[0] = 100) 先计算了i的值为0,而非 data[2] = 100

特殊只写变量“_”, 用于忽略值占位。

  1. func test() (int, string){
  2. return 1, "abc"
  3. }
  4. func main(){
  5. _, s := test()
  6. println(s)
  7. }

注意重新赋值与定义新同名变量的区别。

  1. func main(){
  2. s := "abc"
  3. println(&s)
  4. s, y := "hello", 20 // 重新赋值:与前s在同一命名空间,且有变量定义。
  5. {
  6. s, z := 1000, 30
  7. println(&s, z) // 定义新的同名变量,与前s不在同一命名空间
  8. }
  9. }

1.2 常量

常量值必须是编译器可确定的数字、字符串、布尔值。

  1. const x, y int = 1, 2 // 多常量初始化
  2. const s = "Hello, World !" // 类型判断
  3. const ( // 常量组
  4. a, b = 10, 100
  5. c bool = false
  6. )
  7. func main(){
  8. const x = "xxx" // 未使用的局部常量不会引发编译错误
  9. }

不支持1UL(无符号长整型)、2LL(??)这样的类型后缀。

常量组中,如不提供类型和初值,则视作与上一常量相同。

  1. const (
  2. s = "abc"
  3. x // x = "abc"
  4. )

常量值还可以是len, cap (返回切片分配空间的大小), unsafe.Sizeof等编译器即可确定结果的函数返回值。

  1. const (
  2. a = "abc"
  3. b = len(a)
  4. c = unsafe.Sizeof(b)
  5. )

如果常量类型足以存储初值,则不会引发溢出错误。

  1. const (
  2. a byte = 100 // int to byte
  3. b int = 1e20 // float64 to int , overflows
  4. )

枚举

关键字iota定义常量组中从0开始按行计数的自增枚举值。

  1. const (
  2. Sunday = iota // 0
  3. Monday // 1 即 Monday = iota
  4. Tuesday // 2
  5. Wednesday // 3
  6. Thursday // 4
  7. Friday // 5
  8. Satauday // 6
  9. )
  10. const (
  11. _ = iota // iota = 0
  12. KB int64 = 1 << (10 * iota) // iota = 1 1左移10位 即 2^10 1024
  13. MB // 与KB表达式一样, 但iota = 2 1左移20位 即 2^20
  14. GB
  15. TB
  16. )

在同一常量组中,可以提供多个iota, 它们各自增长。

  1. const (
  2. A, B = iota, iota << 10 // 0, 0 << 10
  3. C, D // 1, 1 << 10
  4. )

如果iota自增被打断,须显式恢复。

  1. const (
  2. A = iota // 0
  3. B // 1
  4. C = "c" // c
  5. D // c, 与上一行相同
  6. E = iota // 4, 显式恢复。 注意计数包含了 C、D 两行。
  7. F // 5
  8. )

可通过自定义类型来实现枚举类型限制。

  1. type Color int
  2. const (
  3. Black Color = iota // 枚举类型为Color
  4. Red
  5. Blue
  6. )
  7. func test(c Color) {}
  8. func main() {
  9. c := Black
  10. test(c)
  11. x := 1
  12. test(x) // Error: cannot use x (type int) as type Color in function argument
  13. test(1) // 常量会被编译器自动替换。
  14. }

1.3 基本类型

更明确的数字类型命名,支持Unicode, 支持常用的数据结构。

类型 长度 默认值 说明
bool 1 false
byte 1 0 uint8
rune 4 0 Unicode Code Point, int32 用与String数组的切片,存储Unicode字符
int, uint 4 或 8 0 32位或64位
int8, uint8 1 0 -128 ~ 127, 0 ~ 255
int16, uint16 2 0 -32768 ~ 32767, 0 ~ 65535
int32, uint32 4 0 -21亿 ~ 21亿, 0 ~ 42亿
int64, uint64 8 0 相当大
float32 4 0.0
float64 8 0.0
complex64 8 64位复数
complex128 16 128位复数
uintptr 4 或 8 足以存储指针的uint32 或 uint64 整数
array 值类型
struct 值类型
string "" UTF-8字符串
slice nil 引用类型
map nil 引用类型
channel nil 引用类型
interface nil 接口
function nil 函数

支持八进制、十六进制,以及科学计数法。标准库math定义了各数字类型取值范围。

  1. a, b, c, d := 071, 0xF, 1e9, math.MinInt16 // 0开头 八进制 0x开头 十六进制

空指针值nil, 而非NULL。

1.4 引用类型

引用类型包括 slice、 map 和 channel。它们有复杂的内部结构,除了申请内存以外,还需要初始化相关属性。

内置函数new计算类型大小,为起分配零值内存,返回指针。
而make会被编译器翻译为具体的创建函数,由其分配内存和初始化成员结构,返回对象而非指针。

  1. a := []int{0, 0, 0} // 提供初始化表达式
  2. a[1] = 10
  3. b := make([]int, 3) // makeslice
  4. b[1] = 10
  5. c := new([]int)
  6. c[1] = 10 // Error: invalid operation: c[1] (index of type *[]int)

1.5 类型转换

不支持隐式类型转换,即便是从窄转宽(不丢失精度)也不行。

  1. var b byte = 100
  2. // var n int = b // Error: cannot use b (type byte) as type int in assignment
  3. var n int = int(b) // 显示转换 窄向宽

使用括号避免优先级错误。

  1. *Point(p) // 相当于 *(Point(p)) 将p地址先转为Point类型,再取Point指向的值
  2. (*Point)(p) // 将p转化为*Point指向的东西
  3. <-chan int(c) // 相当于 <-(chan int(c))
  4. (<-chan int)(c)

同样不能将其他类型当bool值使用。

  1. a := 100
  2. if a { // Error: non-bool a (type int) used as if condition
  3. println("true")
  4. }

1.6 字符串

字符串是不可变值类型,内部用指针指向UTF-8字节数组。

runtime.h

  1. struct String{
  2. byte* str; // 指向byte类型数据的指针
  3. intgo len;
  4. }

使用索引号访问字符(byte)

  1. s := "abc"
  2. println(s[0] == '\x61', s[1] == 'b', s[2] == 0x63) // true, true, true

使用“`”定义不做转义处理的原始字符串,支持跨行。

  1. s := `a
  2. b\r\n\x00
  3. c`
  4. println(s)

输出

  1. a
  2. b\r\n\x00
  3. c

连接跨行字符串时,“+”必须在上一行的末尾,否则引发编译错误。

  1. s := "Hello," +
  2. "World!"
  3. s2 := "Hello,"
  4. + "World!" //Error: invalid operaion: + untyped string

支持用两个索引号返回子串。子串依然指向原字节数组,仅修改了指针和长度属性。

  1. s := "Hello, world!"
  2. s1 := s[:5] // Hello
  3. s2 := s[7:] // world!
  4. s3 := s[1:5] // ello

单引号字符常量表示 Unicode Code Point, 支持 \uFFFF、\U7FFFFFFF、\xFF 格式。对应 rune 类型, UCS-4。

  1. func main(){
  2. fmt.Printf("%T\n", 'a') // %T 类型
  3. var c1, c2 rune = '\u6211', '们'
  4. println(c1 == '我', string(c2) == "\xe4\xbb\xac")
  5. }

输出

  1. int32 // rune 是 int32的别名
  2. true true

要修改字符串,可先将其转换为[]rune或[]byte, 完成后再转换为string。无论哪种方式,都会重新分配内存,并复制字节数组。

  1. func main(){
  2. s := "abcd"
  3. bs := []byte(s)
  4. bs[1] = 'B'
  5. println(string(bs))
  6. u := "电脑"
  7. us := []rune(u)
  8. usp[1] = '话'
  9. println(string(us))
  10. }

输出:

  1. aBcd
  2. 电话

用for 循环遍历字符串时,也支持 byte 和 rune 两种方式。

  1. func main(){
  2. s := "abc汉字"
  3. for i := 0; i < len(s); i++ { //byte
  4. fmt.Printf("%c", s[i])
  5. }
  6. fmt.Println
  7. for _, r := range s { //rune
  8. fmt.Printf("%c", r)
  9. }
  10. }

输出:

  1. a,b,c,,æ,±,,å,-,,
  2. a,b,c,汉,字

1.7 指针

支持指针类型 *T, 指针的指针 **T, 以及包含包名前缀的 *.T

  1. func main {
  2. type data struct { a int }
  3. var d = data{1234}
  4. var p *data
  5. p = &d
  6. fmt.Printf("%p, %v\n", p, p.a) //直接用指针访问目标对象成员,无须转换。
  7. }

输出:

  1. 0x2101ef018, 1234

不能对指针做加减法运算。

  1. x := 1234
  2. p := &x
  3. p++ // Error: invalid operation: p += 1 (mismatched types *int and int)

可以在 unsafe.Pointer 和任何类型指针间进行转换。

  1. func main(){
  2. x := 0x12345678
  3. p := unsafe.Pointer(&x) // *int -> Pointer
  4. n := (*[4]byte)(p) // Pointer -> *[4]byte
  5. for i := 0; i < len(n); i++ {
  6. }
  7. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注