- 从计算机实现角度来看,变量是一段或多段用来存储数据的内存
- go变量总是有固定的数据类型,类型决定了变量内存的长度和存储格式;通过类型转换或指针操作,我们可用不同方式修改变量值,但这并不意味着改变了变量类型;因为内存分配发生在运行期,所以在编码阶段我们用一个易于阅读的名字来表示这段内存。实际上,编译后的机器码从不使用变量名,而是直接通过内存地址来访问目标数据。保存在符号表中的变量名等信息可被删除,或用于输出更详细的错误信息
- 建议以组方式整理多行变量定义
var { x, y int a, s := 100, "abc" }
- 简短模式(:=)定义变量只能用在函数内部
- 简短模式并不总是重新定义变量,也可能是部分退化的赋值操作,退化赋值的前提条件是:最少有一个新变量被定义,且必须是同一定义域;在处理函数错误返回值时,退化赋值允许我们重复使用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新定义 }
- 在多变量赋值时,首先计算出所有右值,然后再依次完成赋值操作;先计算所有相关值,然后再从左到右依次赋值
func main() { x, y := 1, 2 x, y = y+3, x+2 //先计算出右值y+2,x+2,然后再对x,y变量赋值,x=5,y=3 }
data, i := [3]int{0, 1, 2}, 0 i, data[i] = 2, 100 // (i = 0) -> (i = 2), (data[0] = 100)
- 编译器将未使用的局部变量当做错误,全局变量没有问题
- 使用常量就可用一个易于阅读理解的标识符号来代替“魔法数字”,常量值必须是编译期可确定的字符、字符串、数字或布尔值
- 可在函数代码块中定义常量,不曾使用的常量不会引发编译错误
func main() { const x = 123 println(x) const y = 1.23 //未使用,不会引发编译错误 { const x = "abc" println(x) } } // 输出:123 abc
- 在常量组中如不指定类型和初始化值,则与上一行非空常量右值(表达式文本)相同
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 }
- 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) )
- 自增作用范围为常量组。可在多常量定义中使用多个iota,它们各自单独计数,只须确保组中每行常量的列数量相同即可
const ( _, _ = iota, iota * 10 // 0, 0 * 10 a, b // 1, 1 * 10 c, d // 2, 2 * 10 )
- 如果中断iota自增,必须显式恢复,且后续自增值按行序递增,而非C enum那般按上一取值递增
const ( a = iota // 0 b // 1 c = 100 // 100 d // 100(与上一行常量右值表达式相同) e = iota // 4(恢复iota自增,计数包括c, d) f // 5 )
- 自增默认数据类型为int,可显式指定类型
const ( a = iota //int,0 b float64 = iota //float32,1 c = iota //int(如不显式指定iota,则与b数据类型相同),2 )
- 不同于变量在运行期分配存储内存(非优化状态),常量通常会被编译器在预处理阶段直接展开,作为指令数据使用
- 数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址
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
- 标准库math定义了各数字类型的取值范围,math.MinInt8, math.MaxInt8
- 使用浮点数时,须注意小数位的有效精度,注意不要对浮点数使用 ==
- 别名
// byte alias for uint8 // rune alias for uint32
- 引用类型特指slice,map,channel三种预定义类型
- 内置函数new按指定类型长度分配零值内存,返回指针,并不关心类型内部构造和初始化方式。而引用类型必须使用make函数创建,编译器会将make转换为目标类型专用的创建函数(或指令),以确保完成全部内存分配和相关属性初始化。当然,new函数也可为引用类型分配内存,但这是不完整创建。以字典为例,它仅分配了字典类型本身(实际就是个指针包装)所需内存,并没有分配键值存储内存,也没有初始化散列桶等内部属性,因此它无法正常工作
- 使用 “`” 定义不做转义处理的原始字符串,支持跨行
s := `a b\r\n\x00 c` println(s) // 输出: // a // b\r\n\x00 // c
- unsafe包:https://www.jianshu.com/p/10b8870a9e8e
- 位操作
a := 0 a |= 1 << 2 // 0000100: 在 bit2 设置标志位。 a |= 1 << 6 // 1000100: 在 bit6 设置标志位 a = a &^ (1 << 6) // 0000100: 清除 bit6 标志位。
go 类型 —— 摘自go语言学习笔记
最新推荐文章于 2020-10-28 16:08:49 发布