網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
1、 string的定義
Golang中的string的定義在reflect包下的value.go中,定義如下:
StringHeader 是字符串的運(yùn)行時(shí)表示,其中包含了兩個(gè)字段,分別是指向數(shù)據(jù)數(shù)組的指針和數(shù)組的長(zhǎng)度。
// StringHeader is the runtime representation of a string. // It cannot be used safely or portably and its representation may // change in a later release. // Moreover, the Data field is not sufficient to guarantee the data // it references will not be garbage collected, so programs must keep // a separate, correctly typed pointer to the underlying data. type StringHeader struct { Data uintptr Len int }
2、string不可變
Golang中的字符串是不可變的,不能通過(guò)索引下標(biāo)的方式修改字符串中的數(shù)據(jù):
運(yùn)行代碼,可以看到編譯器報(bào)錯(cuò),string是不可變的
但是能不能進(jìn)行一些騷操作來(lái)改變?cè)氐闹的兀?/p>
package main import ( "fmt" "reflect" "unsafe" ) func main() { a := "hello,world" b := a[6:] bptr := (*reflect.StringHeader) (unsafe.Pointer(&b)) fmt.Println(a) fmt.Println(b) *(*byte)(unsafe.Pointer(bptr.Data)) = '.' fmt.Println(a) fmt.Println(b) } // 運(yùn)行結(jié)果 hello,world world unexpected fault address 0x49d7e3 fatal error: fault [signal 0xc0000005 code=0x1 addr=0x49d7e3 pc=0x4779fa] goroutine 1 [running]: runtime.throw(0x49c948, 0x5) C:/Program Files/Go/src/runtime/panic.go:1117 +0x79 fp=0xc0000dbe90 sp=0xc0000dbe60 pc=0x405fd9 runtime.sigpanic() C:/Program Files/Go/src/runtime/signal_windows.go:245 +0x2d6 fp=0xc0000dbee8 sp=0xc0000dbe90 pc=0x4189f6 main.main() F:/go_workspace/src/code/string_test/main.go:20 +0x13a fp=0xc0000dbf88 sp=0xc0000dbee8 pc=0x4779fa runtime.main() C:/Program Files/Go/src/runtime/proc.go:225 +0x256 fp=0xc0000dbfe0 sp=0xc0000dbf88 pc=0x4087f6 runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc0000dbfe8 sp=0xc0000dbfe0 pc=0x435da1 Process finished with the exit code 2
在上面的代碼中,因?yàn)樵趃o語(yǔ)言中不能進(jìn)行指針的加減運(yùn)算,因此取切片,讓b的Data指針指向’,'所在的位置。然后把"hello,world"中的逗號(hào)改為點(diǎn),但是發(fā)現(xiàn)還是不行,程序直接崩潰了??磥?lái)go語(yǔ)言中的指針得到了大大的限制,設(shè)計(jì)者并不想讓程序員過(guò)度使用指針來(lái)寫(xiě)出一些不安全的代碼。
3、使用string給另一個(gè)string賦值
Golang中的字符串的賦值并不是拷貝底層的字符串?dāng)?shù)組,而是數(shù)組指針和長(zhǎng)度字段的拷貝。例如:當(dāng)我們定義了一個(gè)字符串 a := “hello,world” 然后定義了 b := a 底層所做的操作只是創(chuàng)建了兩個(gè)StringHeader的結(jié)構(gòu)體,它們的Data字段都指向同一段數(shù)據(jù),如下圖:
我們可以利用代碼來(lái)證實(shí)這一點(diǎn):
package main import ( "fmt" "reflect" "unsafe" ) func main() { a := "hello,world" b := a fmt.Println(a) fmt.Println(b) aptr := (*reflect.StringHeader) (unsafe.Pointer(&a)) bptr := (*reflect.StringHeader) (unsafe.Pointer(&b)) fmt.Println("a ptr:", unsafe.Pointer(aptr.Data)) fmt.Println("b ptr:", unsafe.Pointer(bptr.Data)) } // 運(yùn)行結(jié)果 hello, world hello, world a ptr: 0x6bdb76 b ptr: 0x6bdb76
在上面的代碼中,將a和b轉(zhuǎn)換為StringHeader類(lèi)型的指針,然后分別打印出,a和b的Data指針的值,發(fā)現(xiàn)是相同的
那么如果對(duì)a做切片賦值給b呢?
func main() { a := "hello,world" b := a[6:] fmt.Println(a) fmt.Println(b) aptr := (*reflect.StringHeader) (unsafe.Pointer(&a)) bptr := (*reflect.StringHeader) (unsafe.Pointer(&b)) fmt.Println("a ptr:", unsafe.Pointer(aptr.Data)) fmt.Println("b ptr:", unsafe.Pointer(bptr.Data)) } // 運(yùn)行結(jié)果 hello,world world a ptr: 0xd4d849 b ptr: 0xd4d84f
0xd4d849 - 0xd4d84f = 0x000006
顯然,也沒(méi)有分配新的數(shù)組并拷貝數(shù)據(jù),而是將原字符數(shù)組的指針的偏移賦給了b的StringHeader的Data
4、string重新賦值
如果對(duì)一個(gè)已經(jīng)賦值的字符串重新賦值,也不會(huì)修改原內(nèi)存空間,而是申請(qǐng)了新的內(nèi)存空間,對(duì)其賦值,并指向新的內(nèi)存空間。如下圖:
也可以使用代碼來(lái)證實(shí)一下:
package main import ( "fmt" "reflect" "unsafe" ) func main() { a := "hello,world" aptr := (*reflect.StringHeader) (unsafe.Pointer(&a)) fmt.Println("a ptr:", unsafe.Pointer(aptr.Data)) fmt.Println("a len", aptr.Len) a = "hello,golang" newAPtr := (*reflect.StringHeader) (unsafe.Pointer(&a)) fmt.Println("b ptr:", unsafe.Pointer(newAPtr.Data)) fmt.Println("b len:", newAPtr.Len) } // 運(yùn)行結(jié)果 a ptr: 0x3ed7f4 a len 11 b ptr: 0x3edb2c b len: 12
補(bǔ)充:字符串拼接
字符串可以很方便的拼接,像下面這樣:
str := "Str1" + "Str2" + "Str3"
即便有非常多的字符串需要拼接,性能上也有比較好的保證,因?yàn)樾伦址膬?nèi)存空間是一次分配完成的,所以性能消耗主要在拷貝數(shù)據(jù)上。
一個(gè)拼接語(yǔ)句的字符串編譯時(shí)都會(huì)被存放到一個(gè)切片中,拼接過(guò)程需要遍歷兩次切片,第一次遍歷獲取總的字符串長(zhǎng)度,據(jù)此申請(qǐng)內(nèi)存,第二次遍歷會(huì)把字符串逐個(gè)拷貝過(guò)去。
字符串拼接偽代碼如下:
func concatstrings(a []string) string { // 字符串拼接 length := 0 // 拼接后總的字符串長(zhǎng)度 for _, str := range a { length += length(str) } s, b := rawstring(length) // 生成指定大小的字符串,返回一個(gè)string和切片,二者共享內(nèi)存空間 for _, str := range a { copy(b, str) // string無(wú)法修改,只能通過(guò)切片修改 b = b[len(str):] } return s }
因?yàn)閟tring是無(wú)法直接修改的,所以這里使用rawstring()方法初始化一個(gè)指定大小的string,同時(shí)返回一個(gè)切片,二者共享同一塊內(nèi)存空間,后面向切片中拷貝數(shù)據(jù),也就間接修改了string。
rawstring()源代碼如下:
func rawstring(size int) (s string, b []byte) { // 生成一個(gè)新的string,返回的string和切片共享相同的空間 p := mallocgc(uintptr(size), nil, false) stringStructOf(&s).str = p stringStructOf(&s).len = size *(*slice)(unsafe.Pointer(&b)) = slice{p, size, size} return }
總結(jié)
原文鏈接:https://blog.csdn.net/Peerless__/article/details/121209732
相關(guān)推薦
- 2022-12-03 Golang基于sync.Once實(shí)現(xiàn)單例的操作代碼_Golang
- 2022-04-11 項(xiàng)目視圖以及項(xiàng)目小部件的基本用法(View表和Widget表)
- 2022-04-11 Python利用正則表達(dá)式從字符串提取數(shù)字_python
- 2023-01-30 react-router-dom?降低版本的兩種方法詳解_React
- 2023-01-23 C++中引用處理的基本方法_C 語(yǔ)言
- 2023-12-11 Mybatis數(shù)據(jù)庫(kù)操作筆記(Mybatis基礎(chǔ)CRUD代碼)
- 2022-12-12 flutter?Bloc?更新后事件同步與異步詳解_Android
- 2022-05-20 Tomcat下載安裝配置詳細(xì)過(guò)程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支