Post Views: 173
2.1 变量 运行时内存分配操作会确保变量自动初始化为二进制零值 如显式初始化值,可省略变量类型,由编译器推断 退化赋值的前提条件是: 最少有一个新变量被定义,且必须是同一作用域 func main() {
x := 100 //简短模式声明并初始变量
println(&x)
x := 200 //错误: no new variables on left side of :=
println(&x, x)
}
func main() {
x := 100
println(&x, x)
{
x, y := 200, 300 //不同作用域,全部是新变量定义
println(&x, x, y)
}
}
简短模式有些限制 定义变量,同时显式初始化 不能提供数据类型 只能用在函数内部 在处理函数错误返回值时,退化赋值允许我们重复使用err变量,这是相当有益的 package main
import (
"log"
"os"
)
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+3、x+2,然后再对x,y变量赋值
println(x, y)
}
编译器将未使用局部变量当作错误。 2.2 命令 命名建议 以字母或下画线开始,由多个字母、数字和下画线组合而成。 区分大小写。 使用驼峰(camel case)拼写格式 局部变量优先使用短名 不要使用保留关键字 不建议使用预定义常量、类型、内置函数相同的名字 专有名词通常会全部大写。 GO有个名为”_”的特殊成员,通常作为忽略占位符使用,可作表达式左值,无法读取内容 import "strconv"
func main() {
x, _ := strconv.Atoi("12") //忽略Atoi的err返回值
println(x)
}
2.3 常量 const x, y int = 123, 0x22
const s = "hello, world!"
const c = '我' //rune (unicode code point)
const (
i, f = 1, 0.123 //int, float64(默认)
b = false
)
可在函数代码块中定义常量,不曾使用的常量不会引发编译错误 func main() {
const x = 123
println(x)
const y = 1.23 //未使用,不会引发编译错误
{
const x = "abc" //在不同作用域定义同名常量
println(x)
}
}
常量值也可以是某些编译器能计算出结果的表达式,如unsafe.Sizeof,len,cap import "unsafe"
const (
pstrSize = unsafe.Sizeof(uintptr(0))
strSize = len("hello, world!")
)
常量组中如不指定类型和初始化值,则与上一行非空常量右值(表达式文本)相同. import "fmt"
func main() {
const (
x uint16 = 120
y //与上一行x类型,右值相同
s = "abc"
z //与s类型,右值相同
)
fmt.Printf("%T, %v", y, y) //输出类型和值
fmt.Printf("%T, %v", z, z)
}
GO借助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自增,则必须显式恢复。 且后续自增值按行序递增,而非C enum那般按上一取值递增 const (
b = iota //0
b //1
c = 100 // 100
d // 100
e = iota // 4 (恢复iota自增, 计数包括c, d)
f // 5
)
在实际编码中,建议用自定义类型实现用途明确的枚举类型。但这并不能将取值范围限定在预定义的枚举值内 type color byte //自定义类型
const (
black color = iota //指定常量类型
red
blue
)
func test(c color) {
println(c)
}
func main() {
test(red)
test(100) //100 并未招出color/type类型取值范围
x := 2
test(x) //错误: cannot use x (type int) as type color in argument to test
}
变量在运行期分配存储内存(非优化状态),常量通常会被编译器在预处理阶段直接展开。作为指令数据使用 2.4 基本类型 类型 长度 默认值 说明 bool 1 false byte 1 0 uint8 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 complex128 16 rune 4 0 Unicode Code Point, int32 uintptr 4, 8 0 足以存储指针的uint string “” 字符串,默认值为空字符串,而非NULL array 数组 struct 结构体 function nil 函数 interface nil 接口 map nil 字典,引用类型 slice nil 切片,引用类型 channel nil 通道,引用类型
标准库math定义了各数字类型的取值范围 标准库strconv可在不同进制(字符串)间转换 import "strconv"
func main() {
a, _ := strconv.ParseInt("1100100", 2, 32)
b, _ := strconv.ParseInt("0144", 8, 32)
c, _ := strconv.ParseInt("64", 16, 32)
println(a,b,c)
println("0b"+strconv.FormatInt(a, 2))
println("0"+strconv.FormatInt(a, 8))
println("0x"+strconv.FormatInt(a, 16))
}
在官方的语言规范中,专门提到两个别名 byte alias for uint8
rune alias for int32
别名无须转换可直接赋值 2.5 引用类型 引用类型特指slice,map,channel 内置函数new按指定类型长度分配零值内存,返回指针,并不关心类型内部构造和初始化方式。 引用类型则必须使用make函数创建,编译器会将make转换为目标类型专用的创建函数(或指令) func mkslice() []int {
s := make([]int, 0, 10)
s = append(s, 109)
return s
}
func mkmap() map[string]int {
m := make(map[string]int)
m["a"] = 1
return m
}
func main() {
m := mkmap()
println(m["a"])
s := mkslice()
println(s[0])
}
new函数也可以为引用类型分配内存,但这是不完整创建。 2.6 类型转换 除常量,别名类型以及命名类型外,Go强制要求使用显式类型转换。 Go不支持操作符重载 如果转换的目标是指针、单向通道或没有返回值的函数类型, 那么必须使用括号,以避免造成语法分解错误 func main() {
x := 100
p := *int(&x) //错误 cannot convert &x (type *int) to type int
// invalid indirect of int(&x) (type int)
println(p)
}
正确的做法是用括号, 让编译器将*int解析为指针类型 (*int)(p) 如果没有括号 *(int(p))
(<-chan int)(c) <-(chan int(c))
(func())(x) func() x
func()int(x) -> 有返回值的函数类型可省略括号,但依然建议使用
(func()int)(x) 使用括号后,更易阅读
2.7 自定义类型 使用关键字type定义用户自定义类型,包括基于现有基础类型创建,或者是结构体,函数类型等。 type flags byte
const (
read flags = 1 << iota
write
exec
)
func main() {
f := read | exec
fmt.Printf("%b\n", f)
}
即便指定了基础类型,也只表明它们有相同底层数据结构,两者间不存在任何关系,属完全不同的两种类型。除操作符外,自定义类型不会继承基础类型的其他信息(包括方法)。不能视作别名,不能隐式转换,不能直接用于比较表达式 未命名类型 数组,切片,字典,通道等类型与具体元素类型或长度等属性有关,故称作未命名类型。 具有相同声明的未命名类型被视作同一类型 具有相同基类型的指针 具有相同元素类型和长度的数组 具有相同元素类型的切片 具有相同键值类型的字典 具有相同数据类型及操作方向的通道 具有相同字段序列(字段名,字段类型,标签,以及字段顺序)的结构体 具有相同签名(参数和返回值列表,不包括参数名)的函数 具有相同方法集(方法名,方法签名,不包括顺序)的接口(interface) 未命名类型转换规则 所属类型相同 基础类型相同,且其中一个是未命名类型。 数据类型相同,将双向通道赋值给单向通道,且其中一个为未命名类型。 将默认值nil赋值给切片,字典,通道,指针,函数或接口 对象实现了目标接口。
文章导航