go 类型 —— 摘自go语言学习笔记

  1. 从计算机实现角度来看,变量是一段或多段用来存储数据的内存
  2. go变量总是有固定的数据类型,类型决定了变量内存的长度和存储格式;通过类型转换或指针操作,我们可用不同方式修改变量值,但这并不意味着改变了变量类型;因为内存分配发生在运行期,所以在编码阶段我们用一个易于阅读的名字来表示这段内存。实际上,编译后的机器码从不使用变量名,而是直接通过内存地址来访问目标数据。保存在符号表中的变量名等信息可被删除,或用于输出更详细的错误信息
  3. 建议以组方式整理多行变量定义
    var {
    	x, y int
    	a, s := 100, "abc"
    }
    
  4. 简短模式(:=)定义变量只能用在函数内部
  5. 简短模式并不总是重新定义变量,也可能是部分退化的赋值操作,退化赋值的前提条件是:最少有一个新变量被定义,且必须是同一定义域;在处理函数错误返回值时,退化赋值允许我们重复使用err变量(也要符合上述前提条件)
    func main() {
    	x := 100
    	println(&x)   // 0xc820041f28
    
    	x, y := 200, "abc"
    	println(&x, x)   // 0xc820041f28   200
    	
    }
    
    func main() {
    	x := 100
    	println(&x)   // 0xc820041f28
    	{
    		x, y := 200, 300   // 不同作用域,全部是新变量定义
    		println(&x, x)   // 0xc820041f38   200
    	}
    }
    
    func main() {
    	f, err := os.Open("/dev/random")
    	...
    	
    	buf := make([]byte, 1024)
    	n, err := f.Read(buf)   // err退化为赋值,n新定义
    	
    }
    
  6. 在多变量赋值时,首先计算出所有右值,然后再依次完成赋值操作
    func main() {
    	x, y := 1, 2
    	x, y = y+3, x+2  //先计算出右值y+2,x+2,然后再对x,y变量赋值,x=5,y=3
    }
    
  7. 编译器将未使用的局部变量当做错误,全局变量没有问题
  8. 使用常量就可用一个易于阅读理解的标识符号来代替“魔法数字”,常量值必须是编译期可确定的字符、字符串、数字或布尔值
  9. 可在函数代码块中定义常量,不曾使用的常量不会引发编译错误
    func main() {
    	const x = 123
    	println(x)
    
    	const y = 1.23 //未使用,不会引发编译错误
    	
    	{
    		const x = "abc"
    		println(x)
    	}
    }
    // 输出:123 abc
    
  10. 在常量组中如不指定类型和初始化值,则与上一行非空常量右值(表达式文本)相同
    func main() {
    	const (
    		x uint16 = 120
    		y               //与上一行x类型、右值相同
    		s = "abc"
    		z               //与上一行s类型、右值相同
    	)
    	fmt.Printf("%T,%v\n",y,y) // uint16, 120
    	fmt.Printf("%T,%v\n",z,z) // string, abc
    }
    
  11. go没有明确意义上的enum定义,不过可借助iota标识符实现一组自增常量值来实现枚举类型
    const (
    	x = iota   // 0
    	y          // 1
    	z          // 2
    )
    const (
    	_ = iota   // 0
    	KB = 1 << (10 * iota)          // 1 << (10 * 1)
    	MB                            // 1 << (10 * 2)
    	GB                            // 1 << (10 * 3)
    )
    
  12. 自增作用范围为常量组。可在多常量定义中使用多个iota,它们各自单独计数,只须确保组中每行常量的列数量相同即可
    const (
    	_, _ = iota, iota * 10   // 0, 0 * 10
    	a, b          // 1, 1 * 10
    	c, d          // 2, 2 * 10
    )
    
  13. 如果中断iota自增,必须显式恢复,且后续自增值按行序递增,而非C enum那般按上一取值递增
    const (
    	a = iota    // 0
    	b           // 1
    	c = 100     // 100
    	d           // 100(与上一行常量右值表达式相同)
    	e = iota    // 4(恢复iota自增,计数包括c, d)
    	f           // 5
    )
    
  14. 自增默认数据类型为int,可显式指定类型
    const (
    	a    = iota   //int,0
    	b float64 = iota   //float32,1
    	c    = iota   //int(如不显式指定iota,则与b数据类型相同),2
    )
    
  15. 不同于变量在运行期分配存储内存(非优化状态),常量通常会被编译器在预处理阶段直接展开,作为指令数据使用
  16. 数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址
    const x = 100   // 无类型声明的常量
    const y byte = x  // 直接展开x,相当于const y byte = 100
    
    fmt.Printf("%p", &x)  //编译错误,不可以对x取地址, cannot take the address of x
    
    
    const a int = 100   // 显式指定常量类型,编译器会做强类型检查
    const b byte = a    // 错误,cannot use a (type int) as type byte in const initializer
    
  17. 标准库math定义了各数字类型的取值范围,math.MinInt8, math.MaxInt8
  18. 使用浮点数时,须注意小数位的有效精度,注意不要对浮点数使用 ==
  19. 别名
    // byte    alias for uint8
    // rune    alias for uint32
    
  20. 引用类型特指slice,map,channel三种预定义类型
  21. 内置函数new按指定类型长度分配零值内存,返回指针,并不关心类型内部构造和初始化方式。而引用类型必须使用make函数创建,编译器会将make转换为目标类型专用的创建函数(或指令),以确保完成全部内存分配和相关属性初始化。当然,new函数也可为引用类型分配内存,但这是不完整创建。以字典为例,它仅分配了字典类型本身(实际就是个指针包装)所需内存,并没有分配键值存储内存,也没有初始化散列桶等内部属性,因此它无法正常工作

你可能感兴趣的:(Go)