關(guān)于golang?struct?中的?slice?無法原子賦值的問題
golang struct 中的 slice 無法原子賦值
有這樣一個結(jié)構(gòu)體:
type MySt struct{ Field []byte }
我在數(shù)組排序中想要交換值:
func Swap(arr []MySt, i,j int){ arr[i], arr[j] = arr[j], arr[i] }
我猜測,就算其成員 Field 是引用類型,但是引用的指針也會交換,應(yīng)該是沒問題的。
實際測試這里復(fù)制錯誤了。
于是我換個寫法:
func Swap(arr []MySt, i,j int){ arr[i].Field, arr[j].Field = arr[j].Field, arr[i].Field }
上面的代碼仍然是不行。
猜測是編譯期產(chǎn)生的代碼不是類似 memcpy()
這種,而是逐個成員去交換,交換到指針這里時,無法做到原子的交換,從而出了問題。
改成下面的方法,終于對了:
func Swap(arr []MySt, i,j int){ arr[i].Field, arr[j].Field = swapSlice(arr[j].Field, arr[i].Field) } func swapSlice(a, b []byte) ([]byte, []byte) { return b, a }
仍然無法理解我為社么錯了,求指教。
golang struct注意事項
struct注意事項:
1.字段聲明語法同變量,示例: 字段名 字段類型
2.字段的類型可以為:基本類型、數(shù)組或引用類型
3.在創(chuàng)建一個結(jié)構(gòu)體變量后,如果沒有給字段賦值,都對應(yīng)一個零值(默認(rèn)值),布爾類型是false,數(shù)值是0,字符串是""。
數(shù)組類型的默認(rèn)值和它的元素類型相關(guān),比如score[3]int 則為[0,0,0]
指針,slice,和map的零值都為nil,即還沒有分配空間
type Person struct { Name string Age int Score [5]float64 ptr *int slice []int map1 map[string]string } func main() { var p1 Person fmt.Println(p1) fmt.Println() if p1.ptr == nil { fmt.Println("ok1") } if p1.slice == nil { fmt.Println("ok2") } if p1.map1 == nil { fmt.Println("ok3") } //使用 slice 一定要先make p1.slice = make([]int, 10) p1.slice[0] = 100 p1.map1 = make(map[string]string) p1.map1["key1"] = "tom" fmt.Println(p1) }
結(jié)果為:
4.不同結(jié)構(gòu)體變量的字段是獨(dú)立,互不影響,一個結(jié)構(gòu)體變量字段的改變,不影響另外一個。因為結(jié)構(gòu)體是值類型,不是引用類型。
type Monster struct { Name string Age int } func main() { var monster1 Monster monster1.Name = "牛博文" monster1.Age = 500 monster2 := monster1 monster2.Name = "蕪湖" fmt.Println("monster1 = ", monster1) fmt.Println("monster2 = ", monster2) }
結(jié)果:
monster2的改變并沒有影響monster1。但如果想要monster2的改變影響monster1,那么應(yīng)該把monster2變?yōu)橹羔槪?/p>
monster2 := &monster1 monster2.Name = "蕪湖" fmt.Println("monster1 = ", monster1) fmt.Println("monster2 = ", *monster2)
此時結(jié)果為:
5. 不能這樣寫:*p2.Name 會報錯,因為.的運(yùn)算符優(yōu)先級比*高,應(yīng)該:(*p2).Name
6. 結(jié)構(gòu)體中所有字段在內(nèi)存中是連續(xù)的
r1 := Rect{Point{1, 2}, Point{3, 4}} fmt.Println(r1) //r1有四個int,在內(nèi)存中是連續(xù)分布的 //打印地址 fmt.Printf("r1.leftUp.x的地址是 %p\n", &r1.leftUp.x) fmt.Printf("r1.leftUp.y的地址是 %p\n", &r1.leftUp.y) fmt.Printf("r1.righttUp.x的地址是 %p\n", &r1.rightUp.x) fmt.Printf("r1.rightUp.y的地址是 %p\n", &r1.rightUp.y) fmt.Println() //r2有兩個 *Point類型,這兩個*Point類型的本身地址是連續(xù)的,但是他們指向的地址不一定是連續(xù)的 r2 := Rect2{&Point{10, 20}, &Point{30, 40}} //打印地址 fmt.Printf("r2.leftUp 本身的地址是%p\n", &r2.leftUp) fmt.Printf("r2.rightUp 本身的地址是%p\n", &r2.rightUp) fmt.Printf("r2.leftUp 指向的地址是%p\n", r2.leftUp) fmt.Printf("r2.rightUp 指向的地址是%p\n", r2.rightUp) fmt.Printf("r2.leftUp.x的地址是 %p\n", &r2.leftUp.x) fmt.Printf("r2.leftUp.y的地址是 %p\n", &r2.leftUp.y) fmt.Printf("r2.righttUp.x的地址是 %p\n", &r2.rightUp.x) fmt.Printf("r2.rightUp.y的地址是 %p\n", &r2.rightUp.y)
7. 結(jié)構(gòu)體是用戶單獨(dú)定義的類型,和其他類型進(jìn)行轉(zhuǎn)換時需要有完全相同的字段(名字,個數(shù),類)
type A struct { num int str string } type B struct { num int str string } func main() { var a A var b B a = A(b) fmt.Println(a, b) } //輸出結(jié)果為 {0 } {0 }
8. 結(jié)構(gòu)體進(jìn)行type重新定義(相當(dāng)于取別名), Golang認(rèn)為是新的數(shù)據(jù)類型,但是相互之間可以強(qiáng)轉(zhuǎn)
type Student struct { Name string Age int } type Stu Student func main() { var stu1 Student var stu2 Stu // stu2 = stu1 這樣會報錯,因為golang認(rèn)為Stu是重新定義的結(jié)構(gòu)體 stu1 = Student(stu2) fmt.Println(stu1, stu2) } //輸出結(jié)果為:{ 0} { 0}
9. struct的每個字段上,可以寫上一個tag,該tag可以通過反射機(jī)制獲取,常見的使用場景就是序列化和反序列化
type Monsters struct { Name string `json:"name"` //`json:"name"`就是struct tag Age int `json:"age"` Skill string `json:"skill"` } func main() { //1. 創(chuàng)建一個monster變量 monster := Monsters{"牛魔王", 500, "芭蕉扇"} //2. 將monster變量序列化為 json格式字串 // json.Marshal函數(shù)中使用了反射 jsonMonster, err := json.Marshal(monster) if err != nil { fmt.Println("json 處理錯誤", err) } //如果age, name, skill首字母是小寫,name返回空序列,所以必須要大寫 //但如果某些程序員或用戶不習(xí)慣大寫,非要小些,那么可以在struct定義的時候加上 `json:"name"` //注意:式鍵盤左上角的``,不是引號'' fmt.Println("jsonMonster", jsonMonster) fmt.Println("jsonMonster", string(jsonMonster)) }
創(chuàng)建struct實例的四種方式
方式一:
type Person struct { Name string Age int } func main() { p1 := Person{} p1.Name = "tom" p1.Age = 18 fmt.Println(p1) //{tom 18} }
方式二:
p2 := Person{"marry", 18} fmt.Println(p2) //{marry 18}
方式3:
// var person *person = new (Person) var p3 *Person = new(Person) //因為p3是一個指針,因此標(biāo)準(zhǔn)的給字段賦值 (*p3).Name = "smith" //(*p3).Name = "smith" 也可以這樣寫 p3.Name = "smith" //原因:go的設(shè)計者為了程序員使用方便,底層會對p3.Name = "smith"進(jìn)行處理 //會給 p3 加上取值運(yùn)算(*p3).Name = "smith" p3.Name = "amy" (*p3).Age = 30 fmt.Println(*p3) //{amy 30}
方式4:
// var person *Person = &person{} var person *Person = &Person{} //因為person是一個指針,因此標(biāo)準(zhǔn)的訪問字段的方法 //(*person).Name = "scott" //go的設(shè)計者為了程序員使用方便,也可以person.Name = "scott" //原因和上面一樣,底層會對 person.Name = "scott"進(jìn)行處理,會加上(*person) (*person).Name = "scott" person.Name = "scott~~" (*person).Age = 88 person.Age = 10 fmt.Println(*person)
第3種和第4種方式返回的是 結(jié)構(gòu)體指針
結(jié)構(gòu)體指針訪問字段的標(biāo)準(zhǔn)方式應(yīng)該是:(*結(jié)構(gòu)體指針)字段名,比如:(*person).Name = "tom"
但go做了一個簡化,也支持結(jié)構(gòu)體指針.字段名,比如:preson.Name = "tom "。更符合程序員使用的習(xí)慣,go編譯器底層對person.Name做了轉(zhuǎn)化(*person).Name
到此這篇關(guān)于為什么 golang struct 中的 slice 無法原子賦值的文章就介紹到這了,更多相關(guān)golang struct slice 無法原子賦值內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang實現(xiàn)根據(jù)某個特定字段對結(jié)構(gòu)體的順序進(jìn)行排序
這篇文章主要為大家詳細(xì)介紹了Golang如何實現(xiàn)根據(jù)某個特定字段對結(jié)構(gòu)體的順序進(jìn)行排序,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03Go語言中的goroutine和channel如何協(xié)同工作
在Go語言中,goroutine和channel是并發(fā)編程的兩個核心概念,它們協(xié)同工作以實現(xiàn)高效、安全的并發(fā)執(zhí)行,本文將詳細(xì)探討goroutine和channel如何協(xié)同工作,以及它們在并發(fā)編程中的作用和優(yōu)勢,需要的朋友可以參考下2024-04-04go打包aar及flutter調(diào)用aar流程詳解
這篇文章主要為大家介紹了go打包aar及flutter調(diào)用aar流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03