關(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 是引用類型,但是引用的指針也會交換,應該是沒問題的。
實際測試這里復制錯誤了。
于是我換個寫法:
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)體變量后,如果沒有給字段賦值,都對應一個零值(默認值),布爾類型是false,數(shù)值是0,字符串是""。
數(shù)組類型的默認值和它的元素類型相關(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)體變量的字段是獨立,互不影響,一個結(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,那么應該把monster2變?yōu)橹羔槪?/p>
monster2 := &monster1
monster2.Name = "蕪湖"
fmt.Println("monster1 = ", monster1)
fmt.Println("monster2 = ", *monster2)此時結(jié)果為:

5. 不能這樣寫:*p2.Name 會報錯,因為.的運算符優(yōu)先級比*高,應該:(*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)體是用戶單獨定義的類型,和其他類型進行轉(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)體進行type重新定義(相當于取別名), Golang認為是新的數(shù)據(jù)類型,但是相互之間可以強轉(zhuǎn)
type Student struct {
Name string
Age int
}
type Stu Student
func main() {
var stu1 Student
var stu2 Stu
// stu2 = stu1 這樣會報錯,因為golang認為Stu是重新定義的結(jié)構(gòu)體
stu1 = Student(stu2)
fmt.Println(stu1, stu2)
} //輸出結(jié)果為:{ 0} { 0}9. struct的每個字段上,可以寫上一個tag,該tag可以通過反射機制獲取,常見的使用場景就是序列化和反序列化
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返回空序列,所以必須要大寫
//但如果某些程序員或用戶不習慣大寫,非要小些,那么可以在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是一個指針,因此標準的給字段賦值
(*p3).Name = "smith"
//(*p3).Name = "smith" 也可以這樣寫 p3.Name = "smith"
//原因:go的設(shè)計者為了程序員使用方便,底層會對p3.Name = "smith"進行處理
//會給 p3 加上取值運算(*p3).Name = "smith"
p3.Name = "amy"
(*p3).Age = 30
fmt.Println(*p3) //{amy 30}方式4:
// var person *Person = &person{}
var person *Person = &Person{}
//因為person是一個指針,因此標準的訪問字段的方法
//(*person).Name = "scott"
//go的設(shè)計者為了程序員使用方便,也可以person.Name = "scott"
//原因和上面一樣,底層會對 person.Name = "scott"進行處理,會加上(*person)
(*person).Name = "scott"
person.Name = "scott~~"
(*person).Age = 88
person.Age = 10
fmt.Println(*person)第3種和第4種方式返回的是 結(jié)構(gòu)體指針
結(jié)構(gòu)體指針訪問字段的標準方式應該是:(*結(jié)構(gòu)體指針)字段名,比如:(*person).Name = "tom"
但go做了一個簡化,也支持結(jié)構(gòu)體指針.字段名,比如:preson.Name = "tom "。更符合程序員使用的習慣,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)體的順序進行排序
這篇文章主要為大家詳細介紹了Golang如何實現(xiàn)根據(jù)某個特定字段對結(jié)構(gòu)體的順序進行排序,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-03-03
Go語言中的goroutine和channel如何協(xié)同工作
在Go語言中,goroutine和channel是并發(fā)編程的兩個核心概念,它們協(xié)同工作以實現(xiàn)高效、安全的并發(fā)執(zhí)行,本文將詳細探討goroutine和channel如何協(xié)同工作,以及它們在并發(fā)編程中的作用和優(yōu)勢,需要的朋友可以參考下2024-04-04
go打包aar及flutter調(diào)用aar流程詳解
這篇文章主要為大家介紹了go打包aar及flutter調(diào)用aar流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03

