<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                Go中的 struct: 可將類型分為命名和未命名兩大類。命名類型包括 bool、int、string 等,而 array、slice、map 等和具體元素類型、長度等有關,屬于未命名類型。 具有相同聲明的未命名類型被視為同一類型。 ~~~ ? 具有相同基類型的指針。 ? 具有相同元素類型和長度的 array。 ? 具有相同元素類型的 slice。 ? 具有相同鍵值類型的 map。 ? 具有相同元素類型和傳送方向的 channel。 ? 具有相同字段序列 (字段名、類型、標簽、順序) 的匿名 struct。 ? 簽名相同 (參數和返回值,不包括參數名稱) 的 function。 ? 方法集相同 ( 方法名、方法簽名相同,和次序無關) 的 interface。 ~~~ struct 特點: ~~~ 1. 用來自定義復雜數據結構 2. struct里面可以包含多個字段(屬性) 3. struct類型可以定義方法,注意和函數的區分 4. struct類型是值類型 5. struct類型可以嵌套 6. Go語言沒有class類型,只有struct類型 7. 結構體是用戶單獨定義的類型,不能和其他類型進行強制轉換 8. golang中的struct沒有構造函數,一般可以使用工廠模式來解決這個問題。 9. 我們可以為struct中的每個字段,寫上一個tag。這個tag可以通過反射的機制獲取到,最常用的場景就是json序列化和反序列化。 ~~~ 概述 與C語言 struct 一樣,與java/php等class類似,在Go中,用于擴展類型,面向對象編程(本部分暫未做詳細介紹)等 可用 type 在全局或函數內定義新類型。 新類型聲明格式:(是類型的組合) ~~~ package main import ( "fmt" "reflect" ) type bigint int64 func main() { var x bigint = 100 fmt.Printf("x 的值是:%v\n", x) fmt.Printf("x 的類型是:%v\n", reflect.TypeOf(x)) type smallint int8 var y smallint = 1 fmt.Printf("y 的值是:%v\n", y) fmt.Printf("y 的類型是:%v\n", reflect.TypeOf(y)) } ~~~ 輸出結果: ~~~ x 的值是:100 x 的類型是:main.bigint y 的值是:1 y 的類型是:main.smallint ~~~ 新類型不是原類型的別名,除擁有相同數據存儲結構外,它們之間沒有任何關系,不會持有原類型任何信息。除非目標類型是未命名類型,否則必須顯式轉換。 ~~~ package main import ( "fmt" "reflect" ) type bigint int64 type myslice []int func main() { x := 1234 var b bigint = bigint(x) // 必須顯式轉換,除非是常量。 var b2 int64 = int64(b) fmt.Printf("b2 的值是:%v , b2 的類型是:%v\n", b2, reflect.TypeOf(b2)) var s myslice = []int{1, 2, 3} // 未命名類型,隱式轉換。 var s2 []int = s fmt.Printf("s2 的值是:%v , s2 的類型是:%v\n", s2, reflect.TypeOf(s2)) } ~~~ 輸出結果: ~~~ b2 的值是:1234 , b2 的類型是:int64 s2 的值是:[1 2 3] , s2 的類型是:[]int ~~~ struct聲明及初始化: 聲明: ~~~ type typeName struct { //... } ~~~ ~~~ package main type global struct{} func main() { type local struct{} } ~~~ 初始化: 方法有幾種: ~~~ package main import ( "fmt" ) type Test struct { int string } var a Test func main() { b := new(Test) //同 var b *Test = new(Test) c := Test{1, "c"} d := Test{} e := &Test{} f := &Test{2, "f"} //同 var d *Test = &Test{2, "f"} fmt.Println(a, b, c, d, e, f) // 注: a b c d 返回 Test 類型變量;e f 返回 *Test 類型變量;若無初始化值,則默認為零值 } ~~~ 輸出結果: ~~~ {0 } &{0 } {1 c} {0 } &{0 } &{2 f} ~~~ 初始化值可以分為兩種: ~~~ a. 有序: typeName{value1, value2, ...} 必須一一對應 b. 無序: typeName{field1:value1, field2:value2, ...} 可初始化部分值 ~~~ 例: ~~~ package main import ( "fmt" ) func main() { type Person struct { name string age int } p := Person{"James", 23} //有序 // 順序初始化必須包含全部字段,否則會出錯。 // p1 := Person{"James"} // Error: too few values in struct initializer p2 := Person{age: 23} //無序 fmt.Println(p) fmt.Println(p2) } ~~~ 輸出結果: ~~~ {James 23} { 23} ~~~ 操作 ~~~ 聲明的struct與普通類型一樣 訪問結構體中的一個變量名, 用 "." 來連接: varName.field 或 (*varName).field 如操作上面 Person 結構體中的 age : p.age = 35 也可以作為函數中的參數,返回值類型 ~~~ 代碼: ~~~ package main import "fmt" //1. 聲明一個自定義類型名為 Person 的結構體 type Person struct { name string age int } func main() { //2. 初始化 var p1 Person p2 := Person{} p3 := Person{"James", 23} p4 := Person{age: 23} fmt.Println(p1, p2, p3, p4) p5 := new(Person) p6 := &Person{} p7 := &Person{"James", 23} p8 := &Person{age: 23} fmt.Println(p5, p6, p7, p8) //3. 操作 p1.age = 50 p2.age = 25 if compareAge(p1, p2) { fmt.Println("p1 is older than p2") } else { fmt.Println("p2 is older than p1") } } func compareAge(p1, p2 Person) bool { if p1.age > p2.age { return true } return false } ~~~ 輸出: ~~~ { 0} { 0} {James 23} { 23} &{ 0} &{ 0} &{James 23} &{ 23} p1 is older than p2 ~~~ struct的內存布局 struct中的所有字段在內存是連續的,布局如下: ![](https://box.kancloud.cn/d8cd52d7dcb76134bbfda7f1b789af04_816x367.png) 匿名字段: 聲明一個 struct1 可以包含已經存在的 struct2 或者go語言中內置類型作為內置字段,稱為匿名字段,即只寫了 typeName,無 varName,但是 typeName 不能重復。 匿名字段與面向對象程序語言中的繼承 聲明及初始化: ~~~ package main import ( "fmt" ) type Person struct { name string age int addr string } type Employee struct { Person //匿名字段 salary int int //用內置類型作為匿名字段 addr string //類似于重載 } func main() { em1 := Employee{Person{"rain", 23, "qingyangqu"}, 5000, 100, "gaoxingqu"} fmt.Println(em1) // var em2 Person = em1 // Error: cannot use em1 (type Employee) as type Person in assignment (沒有繼承, 然也不會有多態) var em2 Person = em1.Person // 同類型拷貝。 fmt.Println(em2) } ~~~ 輸出結果: ~~~ {{Murphy 23 帝都} 5000 100 北京} {Murphy 23 帝都} ~~~ 操作 ~~~ 訪問方式也是通過 "." 來連接 相同字段采用最外層優先訪問,類似于重載 em1.addr 訪問的是 Employee 中最外層的 addr em1.Person.addr 訪問的是 Employee 中 Person 中的 addr ~~~ ~~~ package main import "fmt" type Person struct { name string age int addr string } type Employee struct { Person //匿名字段 salary int int //用內置類型作為匿名字段 addr string //類似于重載 } func main() { /* var em1 Employee = Employee{} em1.Person = Person{"rain", 23, "帝都"} em1.salary = 5000 em1.int = 100 //使用時注意其意義,此處無 em1.addr = "北京" */ //em1 := Employee{Person{"rain", 23, "帝都"}, 5000, 100, "北京"} //初始化方式不一樣,但是結果一樣 em1 := Employee{Person: Person{"Murphy", 23, "帝都"}, salary: 5000, int: 100, addr: "北京"} fmt.Println(em1) fmt.Println("live addr(em1.addr) = ", em1.addr) fmt.Println("work addr(em1.Person.addr) = ", em1.Person.addr) em1.int = 200 //修改匿名字段的值 } ~~~ 輸出: ~~~ {{Murphy 23 帝都} 5000 100 北京} live addr(em1.addr) = 北京 work addr(em1.Person.addr) = 帝都 ~~~ 空結構 "節省" 內存, 如用來實現 set 數據結構,或者實現沒有 "狀態" 只有方法的 "靜態類"。 ~~~ package main func main() { var null struct{} set := make(map[string]struct{}) set["a"] = null } ~~~ 不能同時嵌入某一類型和其指針類型,因為它們名字相同。 ~~~ package main type Resource struct { id int } type User struct { *Resource // Resource // Error: duplicate field Resource name string } func main() { u := User{ &Resource{1}, "Administrator", } println(u.id) println(u.Resource.id) } ~~~ 輸出結果: ~~~ 1 1 ~~~ struct與tag應用 在處理json格式字符串的時候,經常會看到聲明struct結構的時候,屬性的右側還有小米點括起來的內容。形如: ~~~ type User struct { UserId int `json:"user_id" bson:"user_id"` UserName string `json:"user_name" bson:"user_name"` } ~~~ 這個小米點里的內容是用來干什么的呢? struct成員變量標簽(Tag)說明 要比較詳細的了解這個,要先了解一下golang的基礎,在golang中,命名都是推薦都是用駝峰方式,并且在首字母大小寫有特殊的語法含義:包外無法引用。但是由經常需要和其它的系統進行數據交互,例如轉成json格式,存儲到mongodb啊等等。這個時候如果用屬性名來作為鍵值可能不一定會符合項目要求。 所以呢就多了小米點的內容,在golang中叫標簽(Tag),在轉換成其它數據格式的時候,會使用其中特定的字段作為鍵值。例如上例在轉成json格式: ~~~ package main import ( "encoding/json" "fmt" ) type User struct { UserId int UserName string } type UserTag struct { UserId int `json:"user_id" bson:"user_id"` UserName string `json:"user_name" bson:"user_name"` } func main() { u := &User{UserId: 1, UserName: "Murphy"} j, _ := json.Marshal(u) fmt.Printf("struct User echo : %v\n", string(j)) u_tag := &UserTag{UserId: 1, UserName: "Murphy"} j_tag, _ := json.Marshal(u_tag) fmt.Printf("struct UserTag echo : %v\n", string(j_tag)) } ~~~ 輸出結果: ~~~ struct User echo : {"UserId":1,"UserName":"Murphy"} struct UserTag echo : {"user_id":1,"user_name":"Murphy"} ~~~ 可以看到直接用struct的屬性名做鍵值。 其中還有一個bson的聲明,這個是用在將數據存儲到mongodb使用的。 標簽是類型的組成部分。 ~~~ package main var a struct { x int `a` } var b struct { x int `ab` } func main() { a = b } ~~~ 輸出結果: ~~~ ./main.go:11:4: cannot use b (type struct { x int "ab" }) as type struct { x int "a" } in assignment ~~~ ~~~ package main var u1 struct { name string "username" } var u2 struct{ name string } func main() { u1 = u2 } ~~~ 輸出結果: ~~~ ./main.go:9:5: cannot use u2 (type struct { name string }) as type struct { name string "username" } in assignment ~~~ struct成員變量標簽(Tag)獲取 那么當我們需要自己封裝一些操作,需要用到Tag中的內容時,咋樣去獲取呢?這邊可以使用反射包(reflect)中的方法來獲取: ~~~ t := reflect.TypeOf(u) field := t.Elem().Field(0) fmt.Println(field.Tag.Get("json")) fmt.Println(field.Tag.Get("bson")) ~~~ 完整代碼如下: ~~~ package main import ( "encoding/json" "fmt" "reflect" ) func main() { type User struct { //多個Key使用空格進行分開,然后使用Get方法獲取不同Key的值。 UserId int `json:"user_json_id" xml:"user_xml_id"` UserName string `json:"user_json_name" xml:"user_xml_name"` } // 輸出json格式 u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) fmt.Println(string(j)) // 獲取tag中的內容 t := reflect.TypeOf(u) fmt.Println(t) field0 := t.Elem().Field(0) fmt.Println(field0.Tag.Get("json")) fmt.Println(field0.Tag.Get("xml")) field1 := t.Elem().Field(1) fmt.Println(field1.Tag.Get("json")) fmt.Println(field1.Tag.Get("xml")) } ~~~ 輸出結果: ~~~ {"user_json_id":1,"user_json_name":"tony"} *main.User user_json_id user_xml_id user_json_name user_xml_name ~~~ 有意思的struct大小 我們定義一個struct,這個struct有3個字段,它們的類型有byte,int32以及int64,但是這三個字段的順序我們可以任意排列,那么根據順序的不同,一共有6種組合。 ~~~ type user1 struct { b byte i int32 j int64 } type user2 struct { b byte j int64 i int32 } type user3 struct { i int32 b byte j int64 } type user4 struct { i int32 j int64 b byte } type user5 struct { j int64 b byte i int32 } type user6 struct { j int64 i int32 b byte } ~~~ 根據這6種組合,定義了6個struct,分別位user1,user2,…,user6,那么現在大家猜測一下,這6種類型的struct占用的內存是多少,就是unsafe.Sizeof()的值。 大家可能猜測1+4+8=13,因為byte的大小為1,int32大小為4,int64大小為8,而struct其實就是一個字段的組合,所以猜測struct大小為字段大小之和也很正常。 但是,但是,我可以明確的說,這是錯誤的。 為什么是錯誤的,因為有內存對齊存在,編譯器使用了內存對齊,那么最后的大小結果就不一樣了。現在我們正式驗證下,這幾種struct的值。 ~~~ package main import ( "fmt" "unsafe" ) type user1 struct { b byte i int32 j int64 } type user2 struct { b byte j int64 i int32 } type user3 struct { i int32 b byte j int64 } type user4 struct { i int32 j int64 b byte } type user5 struct { j int64 b byte i int32 } type user6 struct { j int64 i int32 b byte } func main() { var u1 user1 var u2 user2 var u3 user3 var u4 user4 var u5 user5 var u6 user6 fmt.Println("u1 size is ", unsafe.Sizeof(u1)) fmt.Println("u2 size is ", unsafe.Sizeof(u2)) fmt.Println("u3 size is ", unsafe.Sizeof(u3)) fmt.Println("u4 size is ", unsafe.Sizeof(u4)) fmt.Println("u5 size is ", unsafe.Sizeof(u5)) fmt.Println("u6 size is ", unsafe.Sizeof(u6)) } ~~~ 輸出結果: ~~~ u1 size is 16 u2 size is 24 u3 size is 16 u4 size is 24 u5 size is 16 u6 size is 16 ~~~ 結果出來了(我的電腦的結果,Mac64位,你的可能不一樣),4個16字節,2個24字節,既不一樣,又不相同,這說明: 內存對齊影響struct的大小 struct的字段順序影響struct的大小 綜合以上兩點,我們可以得知,不同的字段順序,最終決定struct的內存大小,所以有時候合理的字段順序可以減少內存的開銷。 內存對齊會影響struct的內存占用大小,現在我們就詳細分析下,為什么字段定義的順序不同會導致struct的內存占用不一樣。 在分析之前,我們先看下內存對齊的規則: 對于具體類型來說,對齊值=min(編譯器默認對齊值,類型大小Sizeof長度)。也就是在默認設置的對齊值和類型的內存占用大小之間,取最小值為該類型的對齊值。我的電腦默認是8,所以最大值不會超過8. struct在每個字段都內存對齊之后,其本身也要進行對齊,對齊值=min(默認對齊值,字段最大類型長度)。這條也很好理解,struct的所有字段中,最大的那個類型的長度以及默認對齊值之間,取最小的那個。 以上這兩條規則要好好理解,理解明白了才可以分析下面的struct結構體。在這里再次提醒,對齊值也叫對齊系數、對齊倍數,對齊模數。這就是說,每個字段在內存中的偏移量是對齊值的倍數即可。 我們知道byte,int32,int64的對齊值分別為1,4,8,占用內存大小也是1,4,8。那么對于第一個structuser1,它的字段順序是byte、int32、int64,我們先使用第1條內存對齊規則進行內存對齊,其內存結構如下。 bxxx|iiii|jjjj|jjjj user1類型,第1個字段byte,對齊值1,大小1,所以放在內存布局中的第1位。 第2個字段int32,對齊值4,大小4,所以它的內存偏移值必須是4的倍數,在當前的user1中,就不能從第2位開始了,必須從第5位開始,也就是偏移量為4。第2,3,4位由編譯器進行填充,一般為值0,也稱之為內存空洞。所以第5位到第8位為第2個字段i。 第3字段,對齊值為8,大小也是8。因為user1前兩個字段已經排到了第8位,所以下一位的偏移量正好是8,是第3個字段對齊值的倍數,不用填充,可以直接排列第3個字段,也就是從第9位到第16位為第3個字段j。 現在第一條內存對齊規則后,內存長度已經為16個字節,我們開始使用內存的第2條規則進行對齊。根據第二條規則,默認對齊值8,字段中最大類型長度也是8,所以求出結構體的對齊值位8,我們目前的內存長度為16,是8的倍數,已經實現了對齊。 所以到此為止,結構體user1的內存占用大小為16字節。 現在我們再分析一個user2類型,它的大小是24,只是調換了一下字段i和j的順序,就多占用了8個字節,我們看看為什么?還是先使用我們的內存第1條規則分析。 bxxx|xxxx|jjjj|jjjj|iiii 按對齊值和其占用的大小,第1個字段b偏移量為0,占用1個字節,放在第1位。 第2個字段j,是int64,對齊值和大小都是8,所以要從偏移量8開始,也就是第9到16位為j,這也就意味著第2到8位被編譯器填充。 目前整個內存布局已經偏移了16位,正好是第3個字段i的對齊值4的倍數,所以不用填充,可以直接排列,第17到20位為i。 現在所有字段對齊好了,整個內存大小為1+7+8+4=20個字節,我們開始使用內存對齊的第2條規則,也就是結構體的對齊,通過默認對齊值和最大的字段大小,求出結構體的對齊值為8。 現在我們的整個內存布局大小為20,不是8的倍數,所以我們需要進行內存填充,補足到8的倍數,最小的就是24,所以對齊后整個內存布局為 bxxx|xxxx|jjjj|jjjj|iiii|xxxx 所以這也是為什么我們最終獲得的user2的大小為24的原因。 基于以上辦法,我們可以得出其他幾個struct的內存布局。 user3 iiii|bxxx|jjjj|jjjj user4 iiii|xxxx|jjjj|jjjj|bxxx|xxxx user5 jjjj|jjjj|bxxx|iiii user6 jjjj|jjjj|iiii|bxxx 以上給出了答案,推到過程大家可以參考user1和user2試試。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看