[GO language foundation] Go's reference data types, pointers, arrays, structures and pipes (9)

Reference data type

pointer

Get the address of the variable, use &, such as var num int, get the address of num: &num

    var a int = 10
    fmt.Println("a 的地址=" + &a)

Pointer type, the pointer variable stores an address, and the space pointed to by this address stores the value. For example: var ptr int = &num to
get the value pointed to by the pointer type, use:,
for example, var ptr int, use ptr to get ptr Pointed value

    var a int = 10                  //变量
    var ptr *int = &a               //指针变量
    fmt.Printf("ptr 的地址=%v \n", &ptr)
    fmt.Printf("ptr的值是 a 的地址=%v \n", &a)
    fmt.Printf("ptr 所指向地址的值=%v \n", *ptr)

Value types have corresponding pointer types, in the form of * data type, value types include: basic data types , arrays, and structure struct .

    var a int = 10                  //变量
    var ptr *int = &a               //指针变量
    fmt.Printf("ptr 的地址=%v \n", &ptr)

    var b float64 = .123
    var piont *float64 = &b
    fmt.Printf("piont 的地址=%v \n", &piont)

Value type and reference type

distinguish

  • Value types: basic data types (int series, float series, bool, string), arrays and structures
  • Reference types: pointers, slice slices, map, pipe chan, interface, etc. are all reference types

Use characteristics

  • Value type: Variables store values ​​directly, and memory is usually allocated on the stack
  • Reference type: variable stores an address, this address corresponding spatial really stored data (value), the memory is usually in the stack on the dispensing, when there is no variable applied to this address, the address corresponding to the data space becomes a waste, Recovered by GC.

Array

The array can store multiple data of the same type. An array is also a data type, in Go, an array is a value type.

  • Definition and assignment

var array name [array size] data type

    var intArr [3]int //int占8个字节
	//当我们定义完数组后,其实数组的各个元素有默认值 0
	//赋值
	intArr[0] = 10
	intArr[1] = 20
	intArr[2] = 30

    //四种初始化数组的方式
	var numArr01 [3]int = [3]int{1, 2, 3}

	var numArr02 = [3]int{5, 6, 7}

	var numArr03 = [...]int{8, 9, 10}  //这里的 [...] 是规定的写法,不确定大小

	var numArr04 = [...]int{1: 800, 0: 900, 2:999} //下标赋值

    //类型推导
    strArr05 := [...]string{1: "tom", 0: "jack", 2:"mary"}

  • Array layout in memory
  1. The address of the array can be obtained by the name of the array &intArr
  2. The address of the first element of the array is the first address of the array
  3. The address interval of each element of the array is determined by the type of the array, such as int64 -> 8 int32->4.
  • Array traversal
  1. Regular for loop
  2. For-range structure traversal: This is a unique structure of the Go language that can be used to traverse the elements of an array.
    heroes  := [...]string{"宋江", "吴用", "卢俊义"}
	for index, value := range heroes {
		fmt.Printf("index=%v value=%v\n", index , value)
		fmt.Printf("heroes[%d]=%v\n", index, heroes[index])
	}

	for _, v := range heroes {
		fmt.Printf("元素的值=%v\n", v)
	}

      1. 第一个返回值 index是数组的下标
      2. 第二个value是在该下标位置的值
      3. 他们都是仅在 for循环内部可见的局部变量
      4. 遍历数组元素的时 候,如果不想使用下标index,可以直接把下标index标为下划线_
      5. index和value的名称不是固定的,即程序员可以自行指定.一般命名为index和value

  • Precautions
  1. An array is a combination of multiple data of the same type. Once an array is declared/defined, its length is fixed and cannot be changed dynamically
  2. var arr []int declares that an array has no defined length, arr is a slice
  3. The elements in the array can be of any data type, including value types and reference types, but they cannot be mixed.
  4. After the array is created, if no value is assigned, there is a default value (zero value)
    数值类型数组:默认值为 0
    字符串数组:默认值为 ""
    bool 数组: 默认值为 false
  1. Steps to use an array 1. Declare the array and open up space 2 Assign values ​​to each element of the array (default zero value) 3 Use the array
  2. The index of the array starts from 0
  3. The array subscript must be used within the specified range, otherwise it will report panic: the array is out of bounds
  4. Go's array is a value type, and by default it is passed by value, so value copy will be performed. The arrays will not affect each other
  5. If you want to modify the original array in other functions, you can use reference pass (pointer method)
  6. The length is part of the array type, and the length of the array needs to be considered when passing function parameters

Multidimensional Arrays

Two-dimensional array

  • How to use
  1. Declare/define first, then assign
    语法: var 数组名 [大小][大小]类型
    //定义/声明二维数组
	var arr [2][3]int
	//赋初值
	arr[1][2] = 1
	arr[2][1] = 2
	arr[2][3] = 3
  1. Direct initialization
    var 数组名 [大小][大小]类型 = [大小][大小]类型{
   
   {初值..},{初值..}}

    二维数组在声明/定义时也对应有四种写法[和一维数组类似]
    var 数组名 [大小][大小]类型 = [大小][大小]类型{
   
   {初值..},{初值..}}
    var 数组名 [大小][大小]类型 = [...][大小]类型{
   
   {初值..},{初值..}}
    var 数组名 = [大小][大小]类型{
   
   {初值..},{初值..}}
    var 数组名 = [...][大小]类型{
   
   {初值..},{初值..}}

  1. The existence of two-dimensional array in memory
    var arr [2][3]int //以这个为例来分析arr2在内存的布局!!
	arr[1][1] = 10

    fmt.Println(arr) //[[0 0 0] [0 10 0]]
	fmt.Printf("arr[0]的地址%p\n", &arr[0]) //arr[0]的地址0xc000018090
	fmt.Printf("arr[1]的地址%p\n", &arr[1]) //arr[1]的地址0xc0000180a8
    //0xc000018090和 0xc0000180a8 相差 3x8: 3个int元素 (1个int 8byte)

	fmt.Printf("arr[0][0]的地址%p\n", &arr[0][0]) //arr[0][0]的地址0xc000018090
    //&arr2[0] == &arr[0][0] 

	fmt.Printf("arr[1][0]的地址%p\n", &arr[1][0]) //arr[1][0]的地址0xc0000180a8
    &arr[1] == &arr[1][0]

    总结
    1. 二维数组内存形式存储的是指针
    2. 二维数组第一组存储的第一组第一个元素的地址,第二组存储的是第二组第一个元素的地址,依次类推
    3. 二维数组两组地址相差的是一组元素所占的字节

  1. Traversal of two-dimensional array
  2. Double-layer for loop to complete the traversal
    var arr  = [2][3]int{
   
   {1,2,3}, {4,5,6}}

	for i := 0; i < len(arr); i++ {
		for j := 0; j < len(arr[i]); j++ {
			fmt.Printf("%v\t", arr[i][j])
		}
	}

  1. For-range method to complete the traversal
    var arr  = [2][3]int{
   
   {1,2,3}, {4,5,6}}
	for i, v := range arr {
		for j, v2 := range v {
			fmt.Printf("arr[%v][%v]=%v \t",i, j, v2)
		}
	}

slice

  • definition
  1. "Slice" in English is slice
  2. A slice is a reference to an array, so a slice is a reference type. When passing it, it obeys the mechanism of passing by reference.
  3. The use of slices is similar to arrays, traversing the slices, accessing the elements of the slices, and finding the slice length len(slice) are all the same.
  4. The length of the slice can be changed, so the slice is an array that can change dynamically.
  5. The basic syntax of slice definition:
    //var 切片名 []类型
    var a [] int

  • Memory form of slice
  1. slice is indeed a reference type
  2. From the bottom, slice is actually a data structure (struct structure)
    type slice struct {
        ptr *[2]int //截取数组开始位置的地址
        len int //截取的长度
        cap  //容量
    }

  • Use of slices
  1. Define a slice, and then let the slice refer to an array that has been created
    var slice = arr[startIndex:endIndex]
    说明:从 arr 数组下标为 startIndex,取到 下标为 endIndex 的元素(不含 arr[endIndex])。

    var slice = arr[0:end] 可以简写 var slice = arr[:end]
    var slice = arr[start:len(arr)] 可以简写: var slice = arr[start:]
    var slice = arr[0:len(arr)] 可以简写: var slice = arr[:]

  1. Use make to create slices.
    基本语法:var 切片名 []type = make([]type, len, [cap])
    参数说明: type: 就是数据类型 len : 大小 cap :指定切片容量,可选(如果你分配了 cap, 则 cap>=len)

    1. 通过 make 方式创建切片可以指定切片的大小和容量
    2. 如果没有给切片的各个元素赋值,那么就会使用默认值[int , float=> 0   string =>""  bool =>false]
    3. 通过 make 方式创建的切片对应的数组是由 make 底层维护,对外不可见,即只能通过 slice 去访问各个元素.

  1. Define a slice, directly specify the specific array, the principle of use is similar to the way of make
    var strSlice []string = []string{"tom", "jack", "mary"}

Difference between Method 1 and Method 2

Method 1 is to directly reference the array. This array exists in advance and is visible to the programmer.
Method 2 is that the slice is not created by make, and make will also create an array, which is maintained by the slice at the bottom level, which is invisible to the programmer.

  • Slice traversal
  1. for loop traversal in conventional way
    var arr [5]int = [...]int{10, 20, 30, 40, 50}
	slice := arr[1:4]
	for i := 0; i < len(slice); i++ {
		fmt.Printf("slice[%v]=%v ", i, slice[i])
	}

  1. for-range structure traversal slice
    var arr [5]int = [...]int{10, 20, 30, 40, 50}
	slice := arr[1:4]
	for i, v := range slice {
		fmt.Printf("i=%v v=%v \n", i, v)
	}

  • Precautions
  1. When the slice is initialized, var slice = arr[startIndex:endIndex]
    Description: From the arr array subscript as startIndex, get the element with the subscript endIndex (excluding arr[endIndex]).
  2. When the slice is initialized, it still cannot cross the boundary. The range is between [0-len(arr)], but it can grow dynamically.
  3. cap is a built-in function used to count the capacity of a slice, that is, how many elements can be stored at most.
  4. After the slice is defined, it cannot be used because it is empty and needs to be referenced to an array, or make a space for slice to use
  5. Slicing can continue to slice
  6. Use append built-in function to dynamically append slices
    var slice []int = []int{100, 200, 300}
	//通过append直接给slice3追加具体的元素
	slice = append(slice, 400, 500, 600)
	//通过append将切片slice追加给slice
	slice = append(slice, slice...) 

    切片 append 操作的底层原理分析:
    1. 切片 append 操作的本质就是对数组扩容
    2. go 底层会创建一下新的数组 newArr(安装扩容后大小)
    3. 将 slice 原来包含的元素拷贝到新的数组 newArr
    4. slice 重新引用到 newArr
    5. 注意 newArr 是在底层来维护的,程序员不可见.


  1. Slice copy: slices use the copy built-in function to complete the copy
    var slice1 []int = []int{1, 2, 3, 4, 5}
	var slice2 = make([]int, 10)
	copy(slice2, slice1)
	fmt.Println("slice1=", slice1)// 1, 2, 3, 4, 5
	fmt.Println("slice2=", slice2) // 1, 2, 3, 4, 5, 0 , 0 ,0,0,0

    对上面代码的说明:
    1. copy(para1, para2) 参数的数据类型是切片
    2. 按照上面的代码来看, slice4 和 slice5 的数据空间是独立,相互不影响,也就是说 slice4[0]= 999,slice5[0] 仍然是 1

  1. The slice is a reference type, so when it is passed, it obeys the reference passing mechanism.
    var arr [5]int = [...]int{10, 20, 30, 40, 50}
	slice1 := arr[1:4] // 20, 30, 40
    slice2 := slice1[1:2] //[30]
	slice2[0] = 100  // 因为arr , slice1 和slice2 指向的数据空间是同一个,因此slice2[0]=100,其它的都变化

  • string and slice
  1. The bottom layer of string is a byte array, so string can also be sliced
  2. memory form of string
    type slice struct {
        ptr *[4]byte //截取数组开始位置的地址
        len int //截取的长度
    }

  1. string is immutable, which means that the string cannot be modified by str[0] ='z'
  2. If you need to modify the string, you can first convert string -> []byte / or []rune -> modify -> rewrite to string
    str := "hello@world"
    arr1 := []byte(str) 
	arr1[0] = 'z'
	str = string(arr1)

    //我们转成[]byte后,可以处理英文和数字,但是不能处理中文
	// 原因是 []byte 字节来处理 ,而一个汉字,是3个字节,因此就会出现乱码
	// 解决方法是 将  string 转成 []rune 即可, 因为 []rune是按字符处理,兼容汉字

    arr1 := []rune(str) 
	arr1[0] = '北'
	str = string(arr1)

map

A map is a key-value data structure, also known as a field or associative array. Similar to a collection of other programming languages

  • Basic grammar
    var 变量名 map[keytype]valuetype
    
    keytype:
    golang 中的 map,的 key 可以是很多种类型,比如 bool, 数字,string, 指针, channel , 还可以是只包含前面几个类型的 接口, 结构体, 数组 通常 key 为 int 、string
    注意: slice, map, function 不可以,因为这几个没法用 == 来判断

    valuetype:
    valuetype 的类型和 key 基本一样

  • Declaration and use
  1. statement:
    var a map[string]string
    var a map[string]int
    var a map[int]string
    var a map[string]map[string]string
    注意:声明是不会分配内存的,初始化需要 make ,分配内存后才能赋值和使用。

  1. Use:
    Method 1:

        var a map[string]string
        //在使用map前,需要先make , make的作用就是给map分配数据空间
        a = make(map[string]string, 10)
        a["no1"] = "宋江" 
        a["no2"] = "吴用" 
        a["no1"] = "武松" 
        a["no3"] = "吴用" 
    
    

    Way two:

        cities := make(map[string]string)
        cities["no1"] = "北京"
        cities["no2"] = "天津"
        cities["no3"] = "上海"
    
    

    Way three:

        heroes := map[string]string{
            "hero1" : "宋江",
            "hero2" : "卢俊义",
            "hero3" : "吴用",
        }
    
    
  • Add, delete, modify, and check map operations
    cities := make(map[string]string)
    //增
	cities["no1"] = "北京" //如果 key 还没有,就是增加,如果 key 存在就是修改。
	cities["no2"] = "天津"
	cities["no3"] = "上海"

	//删
	delete(cities, "no1")
	//当delete指定的key不存在时,删除不会操作,也不会报错
	delete(cities, "no4")

    //改
    //因为 no3这个key已经存在,因此下面的这句话就是修改
	cities["no3"] = "西安" 

	//查
	val, ok := cities["no2"]
	if ok {
		fmt.Printf("有no1 key 值为%v\n", val)
	} else {
		fmt.Printf("没有no1 key\n")
	}

	//如果希望一次性删除所有的key
	//1. 遍历所有的key,逐一删除 [遍历]
	//2. 直接make一个新的空间
	cities = make(map[string]string)

  • Map traversal
    can only use for-range traversal
    cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	
	for k, v := range cities {
		fmt.Printf("k=%v v=%v\n", k, v)
	}

  • map slice
    //1. 声明一个map切片
	var monsters []map[string]string
	monsters = make([]map[string]string, 2)

    //2. 增加第一个妖怪的信息
	if monsters[0] == nil {
		monsters[0] = make(map[string]string, 2)
		monsters[0]["name"] = "牛魔王"
		monsters[0]["age"] = "500"
	}

    //3. 切片的append函数,可以动态的增加monster
	newMonster := map[string]string{
		"name" : "新的妖怪~火云邪神",
		"age" : "200",
	}
	monsters = append(monsters, newMonster)

  • map sort
    map1 := make(map[int]int, 10)
	map1[10] = 100
	map1[1] = 13
	map1[4] = 56
	map1[8] = 90

    //如果按照map的key的顺序进行排序输出
    //1. 先将map的key 放入到 切片中
	var keys []int
	for k, _ := range map1 {
		keys = append(keys, k)
	}

	//2. 对切片排序 
	sort.Ints(keys)
	fmt.Println(keys)

    //3. 遍历切片,然后按照key来输出map的值
	for _, k := range keys{
		fmt.Printf("map1[%v]=%v \n", k, map1[k])
	}

  • Usage details
  1. Map is a reference type, and it obeys the mechanism of reference type transfer. After a function receives a map, after modification, it will directly modify the original
  2. After the capacity of the map is reached, if you want to add elements to the map, it will automatically expand without panic, which means that the map can dynamically grow key-value pairs (key-value).
  3. The value of the map also often uses the struct type, which is more suitable for managing complex data (better than the previous value is a map),

Structure

  • Golang language object-oriented programming instructions
  1. Golang also supports object-oriented programming (OOP), but it is different from traditional object-oriented programming and is not a pure object-oriented language. Therefore, it is more accurate to say that Golang supports object-oriented programming features.
  2. Golang has no classes. The struct of the Go language has the same status as the classes of other programming languages. You can understand that Golang implements OOP features based on struct.
  3. Golang object-oriented programming is very concise, removing the inheritance of traditional OOP language, method overloading, constructor and destructor, hidden this pointer, etc.
  4. Golang still has the inheritance, encapsulation and polymorphism features of object-oriented programming, but the implementation method is different from other OOP languages, such as inheritance: Golang does not have the extends keyword, and inheritance is achieved through anonymous fields.
  5. Golang Object-Oriented (OOP) is very elegant, OOP itself is a part of the language type system (type system), through the interface (interface) association, low coupling, and very flexible. Later students will fully appreciate this characteristic. In other words, interface-oriented programming is a very important feature in Golang.
  • The difference and connection between structure and structure variable (instance)
  1. The structure is a custom data type that represents a class of things.
    type Cat struct {
        Name string 
        Age int 
        Color string 
        Hobby string
        Scores [3]int
    }

  1. Structure variables (instances) are concrete, actual, and represent a concrete variable
    var cat1 Cat
	
	cat1.Name = "小白"
	cat1.Age = 3
	cat1.Color = "白色"
	cat1.Hobby = "吃<・)))><<"

  • The layout of structure variables (instances) in memory
    var cat1 Cat  // var a int
	
	cat1.Name = "小白"
	cat1.Age = 3
	cat1.Color = "白色"
	cat1.Hobby = "吃<・)))><<"
	fmt.Printf("cat1的地址=%p\n", &cat1)

	fmt.Printf("cat1.Name的地址=%p\n", &cat1.Name)
	fmt.Printf("cat1.Age的地址=%p\n", &cat1.Age)
	fmt.Printf("cat1.Color的地址=%p\n", &cat1.Color)
	fmt.Printf("cat1.Hobby的地址=%p\n", &cat1.Hobby)

    结果:
    cat1的地址=0xc00007e000    //824634236928
    cat1.Name的地址=0xc00007e000 //824634236928 //string占位 16byte
    cat1.Age的地址=0xc00007e010 //824634236944 //int占位 8byte
    cat1.Color的地址=0xc00007e018 //824634236952
    cat1.Hobby的地址=0xc00007e028 //824634236968

    总结:
    &cat1 == &cat1.Name 
    &cat1.Name + 16 = &cat1.Age
    &cat1.Age + 8 = &cat1.Color

  • Declaration structure
    基本语法
    type 结构体名称 struct {
        字段1 type //结构体字段 = 属性 = field 
        字段2 type
    }
    举例:
    type Student struct { //结构体和字段名大写代表public,小写表示private
        Name string
        Age int
        Score float32
    }

    字段 :字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。
    字段细节说明
    1) 字段声明语法同变量,示例:字段名 字段类型
    2) 字段的类型可以为:基本类型、数组或引用类型
    3) 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面讲的一样:
        布尔类型是 false ,数值是 0 ,字符串是 ""。
        数组类型的默认值和它的元素类型相关,比如 score [3]int 则为[0, 0, 0]
        指针,slice,和 map 的零值都是 nil ,即还没有分配空间。
    4) 不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个, 结构体是值类型。

  • Create structure variables and access structure fields
  1. Direct declaration
    var person Person

    var person Person = Person{}

    举例:
    p2 := Person{"mary", 20}

    var person *Person = new (Person)
    (*p3).Name = "smith"  //(*p3).Name = "smith" 也可以这样写 p3.Name = "smith"
    /*
    * 原因: go的设计者 为了程序员使用方便,底层会对 p3.Name = "smith" 进行处理
	* 会给 p3 加上 取值运算 (*p3).Name = "smith"
    */

    var person *Person = &Person{}
    //var person *Person = &Person{"mary", 60} 
    (*person).Name = "scott"  //person.Name = "scott"
	(*person).Age = 88  //person.Age = 88
    

  • Precautions
  1. All fields of the structure are continuous in memory.
    If there are two Point types, the addresses of the two Point types are also continuous, but the addresses they point to are not necessarily continuous.
  2. The structure is a type defined separately by the user, and it needs to have exactly the same fields (name, number and type) when converting with other types
  3. The structure is redefined by type (equivalent to aliasing). Golang considers it to be a new data type, but it can be forced to convert between each other.
  4. A tag can be written on each field of the struct, and the tag can be obtained through the reflection mechanism. The common usage scenarios are serialization and deserialization.
    package main 
    import "fmt"
    import "encoding/json"

    type Monster struct{
        Name string `json:"name"` // `json:"name"` 就是 struct tag
        Age int `json:"age"`
        Skill string `json:"skill"`
    }
    func main() {
        //1. 创建一个Monster变量
        monster := Monster{"牛魔王", 500, "芭蕉扇~"}

        //2. 将monster变量序列化为 json格式字串
        //   json.Marshal 函数中使用反射,这个讲解反射时,我会详细介绍
        jsonStr, err := json.Marshal(monster)
        if err != nil {
            fmt.Println("json 处理错误 ", err)
        }
        fmt.Println("jsonStr", string(jsonStr))
    }

method

The methods in Golang act on the specified data type (ie, bind to the specified data type), so custom types can have methods, not just struct.

  • statement
    func (recevier type) methodName(参数列表) (返回值列表){
        方法体
        return 返回值
    }

    说明:
    1. 参数列表:表示数据类型调用传递给方法的参数
    2. recevier type : 表示这个方法和 type 这个类型进行绑定,或者说该方法作用于 type 类型
    3. receiver type : type 可以是结构体,也可以其它的自定义类型
    4. receiver : 就是 type 类型的一个变量(实例),比如 :Person 结构体 的一个变量(实例)
    5. 返回值列表:表示返回的值,可以多个
    6. 方法主体:表示为了实现某一功能代码块
    7. return 语句不是必须的。


  • transfer
    type A struct {
        Num int
    }
    func (a A) test() {
        fmt.Println(a.Num)
    }
    func main() {
        var a A
        a.test() //调用方法
    }

    说明
    1. func (a A) test() {} 表示 A 结构体有一方法,方法名为 test
    2. (a A) 体现 test 方法是和 A 类型绑定的
    3. test 方法只能通过 A 类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调用
    4.  func (a A) test() {}... a 表示哪个 A 变量调用,这个 a 就是它的副本, 这点和函数传参非常相似。

  • The principle of
    method call and parameter transfer mechanism The method call and parameter transfer mechanism is basically the same as the function. The difference is that when the method is called, the variable of the calling method will be passed to the method as an actual parameter (if the variable is a value type , Then copy the value, if the variable is a reference type, then copy the address).
  • Precautions
  1. The structure type is a value type. In the method call, the transfer mechanism of the value type is complied with, which is the value copy transfer method
  2. If you want to modify the value of the structure variable in the method, you can handle it by the structure pointer
  3. The methods in Golang act on the specified data type (ie: bind to the specified data type), so custom types can have methods, not just struct, such as int, float32, etc. can have methods
  4. The rules for controlling access scope of methods are the same as those for functions. The first letter of the method name is lowercase and can only be accessed in this package. The first letter of the method is capitalized and can be accessed in this package and other packages.
  5. If a type implements the String() method, then fmt.Println will call String() of this variable for output by default

Difference between method and function

  1. The calling method is different
    . The calling method of the function: function name (list of actual parameters)
    The calling method of method: variable. method name (list of actual parameters)
  2. For ordinary functions, when the receiver is a value type, the pointer type data cannot be passed directly, and vice versa
  3. For methods (such as struct methods), when the receiver is a value type, the method can be called directly with a variable of the pointer type, or vice versa.
    type Person struct {
        Name string
    } 

    //函数
    func test01(p Person) {
        fmt.Println(p.Name)
    }
    func test02(p *Person) {
        fmt.Println(p.Name)
    }

    //方法
    func (p Person) test03() {
        p.Name = "jack"
        fmt.Println("test03() =", p.Name) // jack
    }
    func (p *Person) test04() {
        p.Name = "mary"
        fmt.Println("test03() =", p.Name) // mary
    }

    func main() {
        p := Person{"tom"}
        test01(p)
        test02(&p)

        p.test03()
        fmt.Println("main() p.name=", p.Name) // tom
        
        (&p).test03() // 从形式上是传入地址,但是本质仍然是值拷贝
        fmt.Println("main() p.name=", p.Name) // tom

        (&p).test04()
        fmt.Println("main() p.name=", p.Name) // mary
        p.test04() // 等价 (&p).test04 , 从形式上是传入值类型,但是本质仍然是地址拷贝
        fmt.Println("main() p.name=", p.Name) // mary
    }

    总结:
    1. 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和哪个类型绑定.
    2. 如果是和值类型,比如(p Person) , 则是值拷贝; 如果和指针类型,比如是 (p *Person) 则是地址拷贝。

pipeline

interface

Guess you like

Origin blog.csdn.net/weixin_54707168/article/details/114005911