Go(一)结构体


结构体定义

type Point struct{
    X int
    Y float32
    Z string
    s []int
}
1.注意定义结构体时并没有分配内存空间
2.若结构体名称或者成员变量名称首字母为大写,代表这个字段的数据可以被其他包引用
小写为私有,只能在本包使用

内存布局以及分配机制

注:

  • 结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存。实例化即根据结构体定义的格式创建一份与格式一致的内存区域。
  • 在创建一个结构体变量后,若没有给字段赋值,都对应一个零值(默认值),布尔类型是false、整型是0、字符串是""、数组类型默认的和其元素类型相关。
    指针、slice、map的零值是nil,即没有分配空间。
type Person struct {
	Name string
	Age int
	Score [5]float32
	ptr *int
	slice []int
	map1 map[string]string
}
func main(){
	var p1 Person
	fmt.Println(p1)
}

在这里插入图片描述
所以若结构体变量中出现指针、slice、map,在创建结构体变量时,赋值的前提需要为其分配内存空间make(),不然会报错

p1.slice = make([]int,10)
p1.slice[0] = 666

p1.map1 = make(map[string]string)
p1.map1["key1"] = "value1"

var Ptr int
p1.ptr = new(int)
p1.ptr = &Ptr
  • 结构体是值类型,即使是赋值拷贝,不同结构体的变量字段独立,一个结构体的变量字段的改变,不会影响另一个结构体变量的字段
type Animal struct {
	Name string
	Age int
}
func main(){
	var a1 Animal
	var a2 Animal
	a1.Name = "monkey"
	a1.Age = 10
	a2 = a1
	a2.Name = "dog"
	fmt.Println("a1:",a1)
	fmt.Println("a2:",a2)
}
//注意a1和a2是两个独立的数据空间

在这里插入图片描述

重要的栗子:

type Animal struct{
	Name string
	Age int
}
func main(){
	var a1 Animal
	a1.Name = "monkey"
	a1.Age = 10
	var a2 *Animal = &a1
	fmt.Println((*a2).Age)
	fmt.Println(a2.Age)
	a2.Name = "lion"
	fmt.Printf("a2.Name=%v a1.Name=%v \n",a2.Name,a1.Name)
	fmt.Printf("(*a2).Name=%v a1.Name=%v \n",(*a2).Name,a1.Name)
	fmt.Printf("a1的地址是:%p \n",&a1)
	fmt.Printf("a2的地址是:%p a2的值是:%p \n",&a2,a2)
}

在这里插入图片描述

特别注意:不能像下面那样写,点.号的运算优先级大于星号*,若这样写会先用a2.Age,当然会成功访问,但随后的星号 * 出现会报错

var a2 *Animal = &a1
fmt.Printf(*a2.Age)
  • 结构体的所有字段在内存中是连续的,如下,在我64位的系统里面,一个int型数据数据所占字节数为8个字节,如下结果(地址是16进制的),都是相隔8个字节连续分布的
type Animal struct{
	Name int
	Age int
}
type Family struct {
	leftA,rightA Animal
}
/*
//若是定义的指针类型,这两个指针类型的本身地址也是连续的
//但是它们指向的地址不一定是连续的
type Family2 struct{
   leftA,rigthA *Point
}
*/
func main(){
	a1 := Family{Animal{10,20},Animal{10,19}}
	fmt.Printf("a1.leftA.Name 的地址是:%p \n", &a1.leftA.Name)
	fmt.Printf("a1.leftA.Age 的地址是:%p \n",&a1.leftA.Age)
	fmt.Printf("a1.rightA.Name 的地址是:%p \n",&a1.rightA.Name)
	fmt.Printf("a1.rightA.Age的地址是:%p \n",&a1.rightA.Age)
}

在这里插入图片描述


结构体实例化

①基本实例化形式

var ins T
1.T为结构类型,ins为结构体的实例
2.注意这里实例化一个ins,这个ins已经分配了内存空间

②创建指针类型的结构体

ins := new(T)
T为类型,可以是结构体、整型、字符串等
ins:T类型被实例化后保存到ins变量中,ins的类型为*T,属于指针

③取结构体的地址实例化

推荐使用 var p Point = Point{ }

ins := &T{}
T表示结构体类型
ins为结构体实例,类型为*T,为指针类型

以上三种实例化形式的栗子

type Point struct{
   X int
   Y string
   Val *int
}
//基本实例化
//p1 := Person{10,"monkey",&xxx}
var p1 Point
p1.X = 10
p1.Y = "monkey"

//创建指针类型的结构体
//var p2 Point = new(Point)
p2 := new(Point)
//(*p2).X = 6
p2.X = 6
p2.Y = "monkey"

//取结构体地址实例化
//var p3 *Point := &Point{6,"monkey",&xxx}
//(*p3).X = 6
var version = 1
p3 := &Point{}
p3.X = 6
p3.Y = "monkey"
p3.Val = &version

取地址实例化是最广泛的一种结构体实例化方式,关于取地址实例化可用函数封装其初始化过程

func newPoint(x int,y string,val *int)*Point{
      return &Point{
         X: x,
         Y: y,
         Val:val,
      }
}
var version int
ins = newPoint(
   8,
   "monkey",
   &version
)

初始化结构体成员变量

使用键值对初始化结构体

type People struct{
    name string
    child *People
//relation由People类型取地址后,形成类型为*People的实例
relation := &People{
    name:"爷爷"
    child:&People{
      name:"爸爸"
      child:&People{
        name:"我"
      },
   },
}

使用多个值的列表初始化结构体
要求具有一定的顺序

type Address struct{
    Provice string
    City string
    ZipCode int
    PhoneNumber string
}
addr := Address{
    "四川",
    "成都",
     610102,
    "12",
}
fmt.Println(addr)//{四川 成都 610102 12}

初始化匿名结构体
匿名结构体没有类型名称,无须通过type关键字定义就可以直接使用

package main
import "fmt"

func printMsgType(msg *struct{
    id int
    data string}){
    fmt.Println("%T\n",msg)
    }
func main(){
   msg := &struct{
      id int
      data string
   }{
      1024,
      "monkey",
   }
   printMsgType(msg)
}

结构体注意事项

  • 结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字、个数、类型)
type A struct{
   Num int
}
type B struct{
   Num int
}
func main(){
   var a A
   var b B
   a = A(b)//可以转换
}
  • 结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是可以相互转换
type Point struct{
    x int
    y int
}
type Pot Point

type integer int
func main(){
    var p1 Point
    var p2 Pot
   // p2 = p1//这是错误的
   p2 = Pot(p1)

   var num1  integer = 6
   var num2 int = 8
   //num2 = num1//这是错误的
   num2 = int(num1)
}
  • 在前面结构体定义是提到过,“若结构体名称或者成员变量名称首字母为大写,代表这个字段的数据可以被其他包引用,小写为私有,只能在本包使用”,这里调用了包"encoding/json"的方法json.Marshal(),传入参数结构体Animal的一个变量,而结构体变量里的字段名是大写,即公有,即其可以调用其他包的方法,但是如果改成小写首字母,在输出时,json字符串为空
package main

import (
	"encoding/json"
	"fmt"
)
type Animal struct{
	Name string `json:"name"`
	Age int `json:"age"`
}
/*
type Animal struct{
	name string
	age int
 */
func main(){
	monkey := Animal{"monkey",10}
	jsonMon,err := json.Marshal(monkey)
	if err!=nil{
		fmt.Println("json处理错误",err)
	}
	fmt.Println(jsonMon)
	fmt.Println(string(jsonMon))
}
发布了42 篇原创文章 · 获赞 16 · 访问量 5792

猜你喜欢

转载自blog.csdn.net/qq_43668570/article/details/104024410