雨痕 Go语言学习笔记-类型

变量

1.多变量赋值时,先计算所有相关值,然后再从左到右依次赋值。

data, i := [3]int{0, 1, 2}, 0
i, data[i] = 2, 100 // (i = 0) -> (i = 2), (data[0] = 100)
  1. 编译器会将未使⽤的局部变量当做错误。
var s string // 全局变量没问题。
func main() {
i := 0 // Error: i declared and not used。 (可使⽤ "_ = i" 规避)
}
  1. 注意重新赋值与定义新同名变量的区别。
s := "abc"
println(&s)
s, y := "hello", 20 // 重新赋值: 与前 s 在同⼀层次的代码块中,且有新的变量被定义。
println(&s, y) // 通常函数多返回值 err 会被重复使⽤。
{
s, z := 1000, 30 // 定义新同名变量: 不在同⼀层次代码块。
println(&s, z)
}
输出:
0x2210230f30
0x2210230f30 20
0x2210230f18 30

常量

  1. 未使⽤局部常量不会引发编译错误
func main() {
const x = "xxx" // 未使⽤局部常量不会引发编译错误。
}
  1. 常量值还可以是 len、 cap、 unsafe.Sizeof 等编译期可确定结果的函数返回值。
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(b)
)
  1. 如果常量类型⾜以存储初始化值,那么不会引发溢出错误。
const (
a byte = 100 // int to byte
b int = 1e20 // float64 to int, overflows
)

枚举

  1. 关键字 iota 定义常量组中从 0 开始按⾏计数的⾃增枚举值。
const (
Sunday = iota // 0
Monday // 1,通常省略后续⾏表达式。
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
const (
_ = iota // iota = 0
KB int64 = 1 << (10 * iota) // iota = 1
MB // 与 KB 表达式相同,但 iota = 2
GB
TB
)
在同⼀常量组中,可以提供多个 iota,它们各⾃增⻓。
const (
A, B = iota, iota << 10 // 0, 0 << 10
C, D // 1, 1 << 10
)
  1. 如果 iota ⾃增被打断,须显式恢复。
const (
A = iota // 0
B // 1
C = "c" // c
D // c,与上⼀⾏相同。
E = iota // 4,显式恢复。注意计数包含了 C、 D 两⾏。
F // 5
)
  1. 可通过⾃定义类型来实现枚举类型限制。
type Color int
const (
Black Color = iota
Red
Blue
)
func test(c Color) {}
func main() {
c := Black
test(c)
x := 1
test(x) // Error: cannot use x (type int) as type Color in function argument
test(1) // 常量会被编译器⾃动转换。
}

基本类型

类型 长度 默认值 说明
array 值类型
struct 值类型
string “” UTF-8 字符串
slice 引⽤类型
map 引⽤类型
channel 引⽤类型

引用类型

  1. 引⽤类型包括 slice,map 和 channel。它们有复杂的内部结构,除了申请内存外,还需要初始化相关属性。
  2. 内置函数 new 计算类型⼤⼩,为其分配零值内存,返回指针。⽽ make 会被编译器翻译成具体的创建函数,由其分配内存和初始化成员结构,返回对象⽽⾮指针。

类型转换

  1. 不支持隐式类型转换
  2. 不能混用类型

字符串

  1. 字符串是不可变值类型,内部⽤指针指向 UTF-8 字节数组。要修改字符串,可先将其转换成 []rune 或 []byte,完成后再转换为 string。⽆论哪种转

    换,都会重新分配内存,并复制字节数组。

    • 默认值是空字符串 ""。

    • ⽤索引号访问某字节,如 s[i]。

    • 不能⽤序号获取字节元素指针, &s[i] ⾮法。

    • 不可变类型,⽆法修改字节数组。

    • 字节数组尾部不包含 NULL
  2. 使⽤ "`" 定义不做转义处理的原始字符串,⽀持跨⾏。
  3. 连接跨⾏字符串时, "+" 必须在上⼀⾏末尾,否则导致编译错误。
s := "Hello, " +
"World!"
s2 := "Hello, "
+ "World!" // Error: invalid operation: + untyped string
  1. ⽀持⽤两个索引号返回⼦串。⼦串依然指向原字节数组,仅修改了指针和⻓度属性
  2. ⽤ for 循环遍历字符串时,也有 byte 和 rune 两种⽅式。
func main() {
s := "abc汉字"
for i := 0; i < len(s); i++ { // byte
fmt.Printf("%c,", s[i])
}
fmt.Println()
for _, r := range s { // rune
fmt.Printf("%c,", r)
}
}
输出:
a,b,c,æ,±,,å,­,,
a,b,c,汉,字,

指针

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

    • 默认值 nil,没有 NULL 常量。

    • 操作符 "&" 取变量地址, "
    " 透过指针访问⺫标对象。

    • 不⽀持指针运算,不⽀持 "->" 运算符,直接⽤ "." 访问⺫标成员。
  2. 返回局部变量指针是安全的,编译器会根据需要将其分配在 GC Heap 上。
func test() *int {
x := 100
return &x // 在堆上分配 x 内存。但在内联时,也可能直接分配在⺫标栈。
}

⾃定义类型

  1. 可将类型分为命名和未命名两⼤类。命名类型包括 bool,int,string 等,⽽ array,slice,map 等和具体元素类型、⻓度等有关,属于未命名类型。
  2. 具有相同声明的未命名类型被视为同⼀类型。

    • 具有相同基类型的指针。

    • 具有相同元素类型和⻓度的 array。

    • 具有相同元素类型的 slice。

    • 具有相同键值类型的 map。

    • 具有相同元素类型和传送⽅向的 channel。

    • 具有相同字段序列 (字段名、类型、标签、顺序) 的匿名 struct。

    • 签名相同 (参数和返回值,不包括参数名称) 的 function。

    • ⽅法集相同 (⽅法名、⽅法签名相同,和次序⽆关) 的 interface。
  3. 新类型不是原类型的别名,除拥有相同数据存储结构外,它们之间没有任何关系,不会持有原类型任何信息。除⾮⺫标类型是未命名类型,否则必须显式转换。
x := 1234
var b bigint = bigint(x) // 必须显式转换,除⾮是常量。
var b2 int64 = int64(b)
var s myslice = []int{1, 2, 3} // 未命名类型,隐式转换。
var s2 []int = s

总结

  1. 常量部分,未使⽤局部常量不会引发编译错误,常量值还可以是 len、 cap、 unsafe.Sizeof 等编译期可确定结果的函数返回值。
  2. 枚举需要多看
  3. new和make在初始化变量时的区别
  4. 字符串:遍历有两种:字符遍历和rune,不可变类型
comments powered by Disqus