字串通常有兩種設計,一種是「字元」串,一種是「位元組」串。「字元」串中的每個字都是定長的,而「位元組」串中每個字是不定長的。go 語言裡的字串是「位元組」串,英文本元占用 1 個位元組,非英文本元佔多個位元組。這意味著無法通過位置來快速定位出乙個完整的字元來,而必須通過遍歷的方式來逐個獲取單個字元。
使用「字元」串來表示字串勢必會浪費空間,因為所有的英文本元本來只需要 1 個位元組來表示,用 rune 字元來表示的話那麼剩餘的 3 個位元組都是零。但是「字元」串有乙個好處,那就是可以快速定位。
為了進一步方便讀者理解位元組 byte 和 字元 rune 的關係,我花了下面這張圖
字串可以通過下標來訪問內部位元組陣列具體位置上的位元組,位元組是 byte 型別
package main
import
"fmt"
func
main()
}-----------
e5 98 bb e5 93
8863
6869
6e 61
package main
import
"fmt"
func
main()
}-----------
022075
321704699
7104
8105
9110
1097
對字串進行 range 遍歷,每次迭代出兩個變數 codepoint 和 runevalue。codepoint 表示字元起始位置,runevalue 表示對應的 unicode 編碼(型別是 rune)。
如果字串僅僅是位元組陣列,那字串的長度資訊是怎麼得到呢?要是字串都是字面量的話,長度尚可以在編譯期計算出來,但是如果字串是執行時構造的,那長度又是如何得到的呢?
var s1 = "hello"
// 靜態字面量
var s2 = ""
for i:=0;i<10;i++
fmt.println(len(s1))
fmt.println(len(s2))
為解釋這點,就必須了解字串的記憶體結構,它不僅僅是前面提到的那個位元組陣列,編譯器還為它分配了頭部欄位來儲存長度資訊和指向底層位元組陣列的指標,圖示如下,結構非常類似於切片,區別是頭部少了乙個容量字段。
你可以使用下標來讀取字串指定位置的位元組,但是你無法修改這個位置上的位元組內容。如果你嘗試使用下標賦值,編譯器在語法上直接拒絕你。
package main
func
main()
--------
./main.go:5:7: cannot assign to s[0]
字串在記憶體形式上比較接近於切片,它也可以像切片一樣進行切割來獲取子串。子串和母串共享底層位元組陣列。
package main
import
"fmt"
func
main()
-------
lo wo
在使用 go 語言進行網路程式設計時,經常需要將來自網路的位元組流轉換成記憶體字串,同時也需要將記憶體字串轉換成網路位元組流。go 語言直接內建了位元組切片和字串的相互轉換語法。
package main
import
"fmt"
func
main()
--------
[104
101108
108111
32119
111114
108100]
hello world
從節省記憶體的角度出發,你可能會認為位元組切片和字串的底層位元組陣列是共享的。但是事實不是這樣的,底層位元組陣列會被拷貝。如果內容很大,那麼轉換操作是需要一定成本的。
那為什麼需要拷貝呢?因為位元組切片的底層陣列內容是可以修改的,而字串的底層位元組陣列是唯讀的,如果共享了,就會導致字串的唯讀屬性不再成立。
《快學 Go 語言》第 7 課 誘人的烤串
字串通常有兩種設計,一種是 字元 串,一種是 位元組 串。字元 串中的每個字都是定長的,而 位元組 串中每個字是不定長的。go 語言裡的字串是 位元組 串,英文本元占用 1 個位元組,非英文本元佔多個位元組。這意味著無法通過位置來快速定位出乙個完整的字元來,而必須通過遍歷的方式來逐個獲取單個字元。我...
《快學 Go 語言》第 7 課 冰糖葫蘆串
字串通常有兩種設計,一種是 字元 串,一種是 位元組 串。字元 串中的每個字都是定長的,而 位元組 串中每個字是不定長的。go 語言裡的字串是 位元組 串,英文本元占用 1 個位元組,非英文本元佔多個位元組。這意味著無法通過位置來快速定位出乙個完整的字元來,而必須通過遍歷的方式來逐個獲取單個字元。我...
Go語言趣學指南 lesson7
本章學習目標 1 學會使用10種不同的整數型別 2 學會選擇合適的型別 3 學會使用十六進製制表示和二進位制表示 5種整數型別是有符號的 5種整數型別是無符號的 比較常用的整數型別有int和無符號的uint 整數型別 包括有符號和無符號的 實際上一共8種型別,它們取值範圍各不相同 和架構無關 雖然在...