Data structures and algorithms (Golang implementation) (4) A simple introduction to Golang-structures and methods

Structure and method

1. Values, pointers and references

We now have a program:

package main

import "fmt"

func main() {
    // a,b 是一个值
    a := 5
    b := 6

    fmt.Println("a的值:", a)

    // 指针变量 c 存储的是变量 a 的内存地址
    c := &a
    fmt.Println("a的内存地址:", c)

    // 指针变量不允许直接赋值,需要使用 * 获取引用
    //c = 4

    // 将指针变量 c 指向的内存里面的值设置为4
    *c = 4
    fmt.Println("a的值:", a)

    // 指针变量 c 现在存储的是变量 b 的内存地址
    c = &b
    fmt.Println("b的内存地址:", c)

    // 将指针变量 c 指向的内存里面的值设置为4
    *c = 8
    fmt.Println("a的值:", a)
    fmt.Println("b的值:", b)

    // 把指针变量 c 赋予 c1, c1 是一个引用变量,存的只是指针地址,他们两个现在是独立的了
    c1 := c
    fmt.Println("c的内存地址:", c)
    fmt.Println("c1的内存地址:", c1)

    // 将指针变量 c 指向的内存里面的值设置为4
    *c = 9
    fmt.Println("c指向的内存地址的值", *c)
    fmt.Println("c1指向的内存地址的值", *c1)

    // 指针变量 c 现在存储的是变量 a 的内存地址,但 c1 还是不变
    c = &a
    fmt.Println("c的内存地址:", c)
    fmt.Println("c1的内存地址:", c1)
}

print out:

a的值: 5
a的内存地址: 0xc000016070
a的值: 4
b的内存地址: 0xc000016078
a的值: 4
b的值: 8
c的内存地址: 0xc000016078
c1的内存地址: 0xc000016078
c指向的内存地址的值 9
c1指向的内存地址的值 9
c的内存地址: 0xc000016070
c1的内存地址: 0xc000016078

So a,bis the value of a variable, but ca pointer variable, c1is a reference variable.

If it is &added before the variable a:, c := &ait means that athe memory address of the variable is cpointed to a, it is a pointer variable.

When getting or setting the value of the memory pointed to by the pointer, add in front of the pointer variable *and then assign the value, such as:, *c = 4the variable pointed to by the pointer awill change.

If the pointer variable is assigned to another variable:, c1 := cthen the other variable c1can be called a reference variable, the value it stores is also the memory address, and the memory address points to the variable a. At this time, the reference variable is just a copy of the pointer variable, the two variables are mutually independent.

Value variables can be called value types, and reference variables and pointer variables can be called reference types.

How to declare a variable of reference type (that is, a pointer variable)?

We can add one before the data type *to indicate:

    var d *int

We will only distinguish between variables by value type and reference type in the future.

2. Structure

With the basic data types, it is not enough, so Golangsupport us to define our own data types, structures:

// 结构体
type Diy struct {
    A int64   // 大写导出成员
    b float64 // 小写不可以导出
}

The name of the structure is Diyused type 结构体名字 structto define.

There are some members Aand in the structure b, just like the variable definition, the type int64and are float64placed at the back, there is no need to separate any symbols, only need to wrap. The lower-case members in the structure cannot be used outside the package, that is, they cannot be exported.

When using a structure:

    // 新建结构体,值
    g := diy.Diy{
        A: 2,
        //b: 4.0, // 小写成员不能导出
    }

    // 新建结构体,引用
    k := &diy.Diy{
        A: 2,
    }

    // 新建结构体,引用
    m := new(diy.Diy)
    m.A = 2

You can use the structure according to the basic data type, created above:

    g := diy.Diy{
        A: 2,
        //b: 4.0, // 小写成员不能导出
    }

Is a value type structure.

You can also use the structure value in front of one &or use newto create a reference type structure, such as:

    // 新建结构体,引用
    k := &diy.Diy{
        A: 2,
    }

    // 新建结构体,引用
    m := new(diy.Diy)
    m.A = 2

What is the difference between reference and value type structures?

We know that the variables inside and outside the function are independent. When the parameter is passed into the function, the parameter is a copy of the value. The variable in the function is constrained in the function body. Even if the value of the variable passed in the function is modified, the outside of the function I couldn't find it.

But the variable of the reference type is passed by value when it is passed into the function, but the memory address of the reference type is copied. It can be said that a reference is copied. This reference points to a structure outside the function. Use this reference in the function Modify the value of the structure inside, the outside function will also be found.

If the incoming structure is not a reference type structure, but a value type structure, then a complete copy of the structure will be made, and the structure is not related to the original structure.

The built-in data type slices sliceand dictionaries mapare reference types and do not require any additional operations, so passing these two types as function parameters is more dangerous, and you need to be cautious when developing.

Three, methods

A structure can be bound to a function, which means that this function can only be used by the structure. This function is called a structure method:

// 引用结构体的方法,引用传递,会改变原有结构体的值
func (diy *Diy) Set(a int64, b float64) {
    diy.A = a
    diy.b = b
    return
}

// 值结构体的方法,值传递,不会改变原有结构体的值
func (diy Diy) Set2(a int64, b float64) {
    diy.A = a
    diy.b = b
    return
}

It's just that on the basis of the previous function func Set(a int64, b float64), it becomes func (diy *Diy) Set(a int64, b float64), but in the function, you can use diythe members in the structure variable .

The structure of the value type above diy Diycan use the Set2method, and the structure of the reference type diy *Diycan use the Setmethod.

If this is the case, every time we use a structure method, we must pay attention to whether the structure is a value or a reference type. Fortunately, we have Golangbroken our hearts. Each time a method is called using a structure, the structure will be automatically converted To adapt the method. For example, the following:

    // 新建结构体,值
    g := diy.Diy{
        A: 2,
        //b: 4.0, // 小写成员不能导出
    }

    g.Set(1, 1)
    fmt.Printf("type:%T:%v\n", g, g) // 结构体值变化

    g.Set2(3, 3)
    fmt.Printf("type:%T:%v\n", g, g) // 结构体值未变化

    // 新建结构体,引用
    k := &diy.Diy{
        A: 2,
    }
    k.Set(1, 1)
    fmt.Printf("type:%T:%v\n", k, k) // 结构体值变化
    k.Set2(3, 3)
    fmt.Printf("type:%T:%v\n", k, k) // 结构体值未变化

The structure is ga value type, you can't call the Setmethod originally , but with the Golanghelp of conversion, we have no sense, and then the value type becomes a reference type. Similarly, it kis a reference type, and the Set2method can still be used .

Earlier we also said that a function is passed in a reference, and the value corresponding to the reference is modified in the function, which will also be found outside the function.

The structure method is also the same, but the structure itself is diffused in the range, and the structure itself can be modified in the method, but if the structure is a value, then the outside world cannot be found after the modification.

Third, the keywords new and make

The keyword is newmainly used to create a reference type structure, only the structure can be used.

Keywords makeare used to create and initialize a slice or dictionary. We can assign directly to use:

    e := []int64{1, 2, 3}                 // slice
    f := map[string]int64{"a": 3, "b": 4} // map

But this direct assignment is relatively crude, because we may not know where and how much data there is when we use it.

Therefore, when we create slices and dictionaries, we can specify the size of the capacity. See example:

    s := make([]int64, 5)
    s1 := make([]int64, 0, 5)
    m1 := make(map[string]int64, 5)
    m2 := make(map[string]int64)
    fmt.Printf("%#v,cap:%#v,len:%#v\n", s, cap(s), len(s))
    fmt.Printf("%#v,cap:%#v,len:%#v\n", s1, cap(s1), len(s1))
    fmt.Printf("%#v,len:%#v\n", m1, len(m1))
    fmt.Printf("%#v,len:%#v\n", m2, len(m2))

After running:

[]int64{0, 0, 0, 0, 0},cap:5,len:5
[]int64{},cap:5,len:0
map[string]int64{},len:0
map[string]int64{},len:0

Slicing can be used make([],占用容量大小,全部容量大小)to define, you can create a slice of capacity size 5, but the actual occupied capacity is 0, for example make([]int64, 0, 5), you reserve 5a space, so that when you slice append, you will not allocate space internally because of insufficient capacity, saving time.

If you omit the following parameters such as make([]int64, 5), then it is equal make([]int64, 5,5), because then the total capacity is equal to the occupied capacity. Built-in language capand lencan view the full capacity size, the occupied capacity size.

Similarly, the dictionary can also specify the capacity and use it make([],容量大小), but it does not have the so-called occupied capacity. It removes this feature because we use slices, which may require five blank initial values, but if the dictionary has no keys, reserve the initial The value has no effect. Omitting the size of the capacity means creating a 0key-value structure with a capacity of 0. When assigning a value, it will automatically allocate space.

Fourth, the difference between built-in syntax and functions, methods

Functions are an encapsulation of code fragments by binding functions and structures.

But Golangthere are some built-in grammar, is not a function, not a method, such as append, , cap, len, makethis is a grammatical features.

The grammatical features are provided by high-level languages, which help you hide details such as how to allocate memory.

Series article entry

I am the star Chen, Welcome I have personally written data structures and algorithms (Golang achieve) , starting in the article to read more friendly GitBook .

Published 13 original articles · praised 0 · visits 95

Guess you like

Origin blog.csdn.net/m0_46803965/article/details/105563289