GoLang之string底层系列三(底层)

首先得有一个起始地址,这样才可以找到字符串内容,但是找得到开头猜不到结尾,内存那么大,天知道它在哪里结束;C语言说"在字符串结尾处放一个特定的字符标识不就好了",C语言用的是编号位0的字符吗,但是这也就限制了内容中不能再出现这个标识符了,否则会发生不可预估的后果,所以Go语言并没有采取这样的做法,而是在起始地址后面多存一个长度,这个长度并不是字符个数,而是字节个数。对于s1来说,s1变量有个起始地址,还有一个字节数目,现在既找得到开头,又定得了结尾,而且不限制字符串内容

image-20220305121051618

额外注意的是,可以像如下这样读取字符串的内容,但是不能像如下一样修改它,一部分原因是Go语言认为字符串内容是不会被修改的,编译器会把这样定义的"s1 :="eggo世界"字符串内容分配到只读内存段,内存中这些读、写、执行等权限是为了保护程序正常运行,但是也限制我们不能修改只读的内容

func main() {
	var b string = "eggo世界"
	for i := 0; i < len(b); i++ {
		fmt.Println(i)
		fmt.Println(b[i])
	}

/*0
101
1
103
2  
103
3  
111
4  
228
5  
184
6  
150
7
231
8
149
9
140
*/


func main() {
	var b string = "eggo世界"
	fmt.Println(b[0]) //101   小写字母的e的ascii码是101

	fmt.Println(b[1])  //103  小写字母的g的ascii码是101
	fmt.Println(b[2])  //103
	fmt.Println(b[3])  //111  小写字母的0的ascii码是111
	fmt.Println(b[4])  //228
	fmt.Println(b[5])  //184
	fmt.Println(b[6])  //150
	fmt.Println(b[7])  //231
	fmt.Println(b[8])  //149
	fmt.Println(b[9])  //140
	fmt.Println(b[10]) //到这里就会抛出错误,超出索引的错误
	fmt.Println(b[11])
}

func main() {
	var b string = "eggo世界"
    //以rune的形式在遍历
	for i, v := range b {
		fmt.Println(i)
		fmt.Println(v)
	}
	/*
	0
	101
	1
	103
	2
	103
	3
	111
	4
	19990
	7
	30028
	*/
}
func main() {
	var b string = "eggo世界"
	b[2]='0'//编译报错:无法分配给b[2]
}

image-20220305121514575

另外,字符串变量是可以共用底层字符串内容的,如果我们通过s1把g改成o后,s2也会被修改,这样的影响是不可预测的

image-20220305122039641

如果非要修改,可以直接给变量整赋新值,它存储的地址就会指向新的内容,并没有去”修改“原来的内存;
也可以把变量强制类型改成字节slice,这样会为slice变量重新分配一段内存,并且会拷贝原来字符串的内容,同样可以脱离只读内存的限制,不过,等我们了解了slice的结构,并且学会了使用unsafe,我们肯定有办法让slice依然使用原来字符串指向的这段内存,这样的话即便转换了类型则依然无法修改这段只读内存的内容


func main() {
	var b string = "11111"
	fmt.Println(b)//输出:11111
	b = "asdfghjk"
	fmt.Println(b) //输出:asdfghjk

}

image-20220305123948130

总结:string由两部分组成,一部分是指向字符串起始地址的指针,另一部分是 字节个数len 注意不是字符个数,是字节个数!这个数据类型占用16B空间,指向字符串起始地址的指针和存在字节个数的整型分别是8B
1.string中的内容不能被修改,go语言认为字符串内容是不会被修改的,编译器会把字符串内容分配到只读内存段
2.可以把string当作字节切片的参数,这样会为slice变量重新分配一段内存,并且会拷贝原来字符串的内容
3.string可能为空,但不会是nil

func main() {
	var a string
	var b string = ""
	fmt.Println("111" + "22222")     //11122222
	fmt.Println("111" + a + "22222") //11122222
	fmt.Println("111" + b + "22222") //11122222

	//这三行输出一样
}