Golang 结构体使用注意事项和细节

Jackey Golang 4,221 次浏览 , , 3条评论

结构体的所有字段在内存中是连续的

代码:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import "fmt"
// 结构体
type Point struct {
x int
y int
}
// 结构体
type Rect struct {
leftUp, rightDown Point
}
// 结构体
type Rect2 struct {
leftUp, rightDown *Point
}
func main() {
r1 := Rect{Point{1, 2}, Point{3, 4}}
// r1有四个int, 在内存中是连续分布
// 打印地址
fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n",
&r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)
// r2有两个 *Point类型,这两个*Point类型的本身地址也是连续的
// 但是他们指向的地址不一定是连续的
r2 := Rect2{&Point{10, 20}, &Point{30, 40}}
// 打印地址
fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n",
&r2.leftUp, &r2.rightDown)
// 他们指向的地址不一定是连续,这个要看系统在运行时是如何分配
fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n",
r2.leftUp, r2.rightDown)
}
package main import "fmt" // 结构体 type Point struct { x int y int } // 结构体 type Rect struct { leftUp, rightDown Point } // 结构体 type Rect2 struct { leftUp, rightDown *Point } func main() { r1 := Rect{Point{1, 2}, Point{3, 4}} // r1有四个int, 在内存中是连续分布 // 打印地址 fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n", &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y) // r2有两个 *Point类型,这两个*Point类型的本身地址也是连续的 // 但是他们指向的地址不一定是连续的 r2 := Rect2{&Point{10, 20}, &Point{30, 40}} // 打印地址 fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n", &r2.leftUp, &r2.rightDown) // 他们指向的地址不一定是连续,这个要看系统在运行时是如何分配 fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n", r2.leftUp, r2.rightDown) }
package main

import "fmt"

// 结构体
type Point struct {
  x int
  y int
}

// 结构体
type Rect struct {
  leftUp, rightDown Point
}

// 结构体
type Rect2 struct {
  leftUp, rightDown *Point
}

func main()  {
  r1 := Rect{Point{1, 2}, Point{3, 4}}

  // r1有四个int, 在内存中是连续分布
  // 打印地址
  fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n",
    &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)

  // r2有两个 *Point类型,这两个*Point类型的本身地址也是连续的
  // 但是他们指向的地址不一定是连续的
  r2 := Rect2{&Point{10, 20}, &Point{30, 40}}

  // 打印地址
  fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n",
    &r2.leftUp, &r2.rightDown)

  // 他们指向的地址不一定是连续,这个要看系统在运行时是如何分配
  fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n",
    r2.leftUp, r2.rightDown)
}

对应分析图:

结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)

代码:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import "fmt"
type A struct {
Num int
}
type B struct {
Num int
}
func main() {
var a A
var b B
a = A(b) // ? 可以转换,但是有要求,就是结构体的字段要完全一样(包括:名字、个数和类型!)
fmt.Println(a, b)
}
package main import "fmt" type A struct { Num int } type B struct { Num int } func main() { var a A var b B a = A(b) // ? 可以转换,但是有要求,就是结构体的字段要完全一样(包括:名字、个数和类型!) fmt.Println(a, b) }
package main

import "fmt"

type A struct {
  Num int
}

type B struct {
  Num int
}

func main()  {
  var a A
  var b B
  a = A(b) // ? 可以转换,但是有要求,就是结构体的字段要完全一样(包括:名字、个数和类型!)
  fmt.Println(a, b)
}

结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转

代码1:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import "fmt"
type Student struct {
Name string
Age int
}
type Stu Student
func main() {
var stu1 Student
var stu2 Stu
stu2 = stu1
// 正确吗? 错误
// 可以这样修改 stu2 = Stu(stu1) //OK
fmt.Println(stu1, stu2)
}
package main import "fmt" type Student struct { Name string Age int } type Stu Student func main() { var stu1 Student var stu2 Stu stu2 = stu1 // 正确吗? 错误 // 可以这样修改 stu2 = Stu(stu1) //OK fmt.Println(stu1, stu2) }
package main

import "fmt"

type Student struct {
  Name string
  Age  int
}

type Stu Student

func main() {
  var stu1 Student
  var stu2 Stu
  stu2 = stu1
  // 正确吗? 错误
  // 可以这样修改 stu2 = Stu(stu1) //OK
  fmt.Println(stu1, stu2)
}

 

代码2:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import "fmt"
type interger int
func main() {
var i interger = 10
var j int = 20
j = i // 正确吗? 不可以! 修改:j = int(i)
fmt.Println(i, j)
}
package main import "fmt" type interger int func main() { var i interger = 10 var j int = 20 j = i // 正确吗? 不可以! 修改:j = int(i) fmt.Println(i, j) }
package main

import "fmt"

type interger int

func main()  {
  var i interger = 10
  var j int = 20
  j = i // 正确吗? 不可以! 修改:j = int(i)
  fmt.Println(i, j)
}

struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是 序列化和反序列化。

序列化的使用场景:

代码:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import (
"encoding/json"
"fmt"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Skill string `json:"skill"`
}
func main() {
// 创建一个Monster 变量
monster := Monster{"牛魔王", 500, "芭蕉扇~"}
// 将monster变量序列化为json格式字符串
// json.Marshal 函数中使用反射
jsonStr, err := json.Marshal(monster)
if err != nil {
fmt.Println("json 处理错误 ", err)
}
fmt.Println("jsonStr ", string(jsonStr))
}
package main import ( "encoding/json" "fmt" ) type Monster struct { Name string `json:"name"` Age int `json:"age"` Skill string `json:"skill"` } func main() { // 创建一个Monster 变量 monster := Monster{"牛魔王", 500, "芭蕉扇~"} // 将monster变量序列化为json格式字符串 // json.Marshal 函数中使用反射 jsonStr, err := json.Marshal(monster) if err != nil { fmt.Println("json 处理错误 ", err) } fmt.Println("jsonStr ", string(jsonStr)) }
package main

import (
  "encoding/json"
  "fmt"
)

type Monster struct {
  Name  string `json:"name"`
  Age   int    `json:"age"`
  Skill string `json:"skill"`
}

func main()  {
  // 创建一个Monster 变量
  monster := Monster{"牛魔王", 500, "芭蕉扇~"}

  // 将monster变量序列化为json格式字符串
  // json.Marshal 函数中使用反射
  jsonStr, err := json.Marshal(monster)
  if err != nil {
    fmt.Println("json 处理错误 ", err)
  }
  fmt.Println("jsonStr ", string(jsonStr))
}

 

3 条评论

  1. 今日头条新闻 2020年2月20日 下午3:17 回复

    文章非常好超喜欢

  2. 卢松松博客 2020年3月11日 下午5:18 回复

    祝你好运哈!

    • gopher 2020年3月11日 下午5:30 回复

      谢谢,也祝你好运哦!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Go