一. string
- golang中对string的解释:
- 8比特字节的集合
- 可以为空(长度为0),但不会是nil
- string对象不可以修改
- 查看string 数据结构: string数据结构跟切片有些类似,例如byte切片,只不过切片还有一个表示容量的成员
//src/runtime/string.go:stringStruct
type stringStruct struct {
//字符串的首地址
str unsafe.Pointer
//字符串的长度
len int
}
- string 拼接源码
// 生成一个新的string,返回的string和切片共享相同的空间
func rawstring(size int) (s string, b []byte) {
p := mallocgc(uintptr(size), nil, false)
stringStructOf(&s).str = p
stringStructOf(&s).len = size
*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}
return
}
其它问题
- string为什么是不可修改的: Go的实现中string不包含内存空间,只有一个内存的指针,好处是string变得非常轻量,可以很方便的进行传递而不用担心内存拷贝, 因为string通常指向字符串字面量,而字符串字面量存储位置是只读段,而不是堆或栈上,所以才有了string不可修改的约定
- []byte转换成string一定会拷贝内存吗: byte切片转换成string时并不会拷贝内存,而是直接返回一个string,这个string的指针(string.str)指向切片的内存
二. iota
基础
- iota是个特殊常量,可以在const中生成自增的整数序列,每个const块中使用时从0开始,并且在每一行常量声明中递增1(不包括空行和注释,但是包括"_"下划线),但不会分配给任何常量。 如果有表达式,iota会作为表达式的一部分进行计算。下方LOG_EMERG值为0,每个常量递增1
type Priority int
const (
LOG_EMERG Priority = iota //0
LOG_ALERT //1
LOG_CRIT //2
LOG_ERR //......
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG //7
)
- 注意点1
- 如果在iota下声明的某一个常量没有继续沿用iota,而是手动指定了一个值,那么声明的这个变量就是手动指定的值
- 如果iota声明的常量序列中,某一个常量手动设置了值,该变量下方相邻的其它变量如果没有重新指定位iota,并且没有手动设置其他值时,那么会继续沿用上方手动指定的值,入下方代码d设置为ss,下方常量e的值也是ss
- 在一个iota声明常量序列中,整个序列中每声明一个常量,不管内部有没有真实使用iota设置,iota都是累加,如下方代码d,e使用的是手动设置的ss,但是iota还是累加了这两次,最终g为5,h为6
const (
a = iota //0
b //1
c //2
//如果在iota下声明的某一个常量没有继续沿用iota
//而是手动指定了一个值,那么声明的这个变量就是指定的值
//并且该变量下方继续声明的变量,入下方的"e"在不重新指定回iota时,
//并且没有手动设置其他值时,那么该变量值与上方指定的值相同,
//所以下方e的值也是ss
d = "ss" //ss
e //ss
//在一个iota声明的变量序列中,虽然上方有的变量没有使用iota,iota还是会默认加一
//重新将iota赋值给g
g = iota //5
h //6
)
- iota与位移
const (
a = iota //使用iota初始值,所以a等于0
b
c
d = 1 << iota //表示iota左移1为,当前iota为3左移1位值为8
e //继续沿用上方的逻辑,iota此时变为4左移1位值位16
f //继续沿用上方逻辑,iota此时为5左移1位置位32
g = iota //重新设置回iota,那么g的值就是6
h = 1e6
)
const (
a= 1 << iota //表示iota左移1位,当前iota值为0,左移1位等于1
b //延续上方iota左移运算,iota此时为1左移1位等于2
c //iota此时为2,左移1位等于4
d = iota //重新设置会iota值为3
e //4
f= 1e6
)
- 下方每个常量值是多少:
mutexLocked = 1, mutexWoken =2;mutexStarving = 4;mutexWaiterShift = 3;starvationThresholdNs =1000000
const (
//iota初始值为0,
//然后执行<<左移操作,将一个数的二进制表示向左移动指定的位数,当前也就是移动1位,mutexLocked 为1
mutexLocked = 1 << iota // mutex is locked
mutexWoken
mutexStarving
mutexWaiterShift = iota
starvationThresholdNs = 1e6
)
- 下方每个常量值是多少:(下划线也会累加iota)
bit0 = 1, mask0 = 0, bit1 = 2, mask1 = 1, bit3 = 8, mask3 = 7
const (
bit0, mask0 = 1 << iota, 1<<iota - 1
bit1, mask1
_, _
bit3, mask3
)
- 根据以上问题引出iota的定义: iota代表了const声明块的行索引(下标从0开始)
const (
bit0, mask0 = 1 << iota, 1<<iota - 1 //const声明第0行,即iota==0
bit1, mask1 //const声明第1行,即iota==1, 表达式继承上面的语句
_, _ //const声明第2行,即iota==2
bit3, mask3 //const声明第3行,即iota==3
)
第0行的表达式展开即bit0, mask0 = 1 << 0, 1<<0 - 1,所以bit0 == 1,mask0 == 0;
第1行没有指定表达式继承第一行,即bit1, mask1 = 1 << 1, 1<<1 - 1,所以bit1 == 2,mask1 == 1;
第2行没有定义常量
第3行没有指定表达式继承第一行,即bit3, mask3 = 1 << 3, 1<<3 - 1,所以bit0 == 8,mask0 == 7
原理
- const块中每一行在GO中使用spec数据结构描述,spec声明如下
ValueSpec struct {
Doc *CommentGroup // associated documentation; or nil
Names []*Ident // value names (len(Names) > 0)
Type Expr // value type; or nil
Values []Expr // initial values; or nil
Comment *CommentGroup // line comments; or nil
}
- 当前只关注ValueSpec.Names,该切片中保存了一行中定义的常量,如果一行定义N个常量,那么ValueSpec.Names切片长度即为N, const块实际上是spec类型的切片,用于表示const中的多行, 伪代码,可以清晰的看出iota实际上是遍历const块的索引,每行中即便多次使用iota,其值也不会递增
for iota, spec := range ValueSpecs {
for i, name := range spec.Names {
obj := NewConst(name, iota...) //此处将iota传入,用于构造常量
...
}
}