Golang Pointer and nil analysis

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Once heard a word, the essence of programming is the pointer and recursion. That will just start coding, but these two concepts have a perceptual superficial understanding. The first contact pointer, no more than C language, can understand the use of good pointers to become a qualified C language basic symbol.

Golang also provides pointers, but go does not perform pointer operations, so there is a lot less complexity relative to C. In private, go provides pointers, not to allow you to deal with memory more, but to provide a basic bridge of operational data. Because go is a lot of calls, often copying a copy of the object, such as the parameters of the function, if there is no pointer, some cases have to have a lot of copies.

Memory and variables

There are generally variables in programming languages. Variables store some values. We usually declare, assign, and destroy variables.

Imagine that memory is like a long table with a lot of contiguous drawers (blocks of memory) in the table. We can sequentially give each drawer starting from 0 (memory address), this number is the address of the drawer. When we need to use a drawer to store things, we can find the corresponding drawer by the number, put things. This is the data we save.

 addr      1          2         3         4         5      +----------+---------+---------+---------+---------+      |          |         |         |         |         |      |          |         |         |         |         |      |  a book  |  none   |   none  |   none  |  a pen  |      |          |         |         |         |         |      |          |         |         |         |         |      +----------+---------+---------+---------+---------+

It is good to find things by number, but sometimes we want to intuitively know what is in the drawer, put a label (declaration) a tag (variable name), such as the number 5 drawer-type fruit, numbered 7 drawer-style book. The next time you're looking for a book, you'll find a drawer with a book label directly.

 addr      1          2         3         4         5      +----------+---------+---------+---------+---------+      |          |         |         |         |         |      |          |         |         |         |         |      |  a book  |  none   |   none  |   none  |  a pen  |      |          |         |         |         |         |      |          |         |         |         |         |      +----------+---------+---------+---------+---------+ tag      book                                     pen

Memory, memory address, memory stored data, variable name, these concepts are almost the basic routines of the computer to execute the program through the programming language. But high-level languages often help us hide the mapping of memory addresses and variable names. Like C, you can declare a variable and then assign a value, but like python, declarations and assignments can even be written together.

Pointer

Knowing the relationship between the memory address and the variable, let's look at the pointer. You can think of a pointer as a "type," a value of this type is a memory address. For example there is a number 3 drawer, which holds a pointer, and the value of this pointer is a number 5, by manipulating the pointer, we can directly manipulate the memory data of number 5.

 addr      1          2         3         4         5      +----------+---------+---------+---------+---------+      |          |         |         |         |         |      |          |         |         |         |         |      |  a book  |  none   | 5 -->   |   none  |  a pen  |      |          |         |         |         |         |      |          |         |         |         |         |      +----------+---------+---------+---------+---------+ tag      book                pointer              pen

Remember, the pointer is a memory address, but the pointer itself also has a memory address. As pointed to other drawers, there is also a drawer to store itself.

Golang the address and value of the pointer

The high-level language provides the perfect binding relationship between declaring variables and values. Help us hide the variable memory address. To get the memory address, you need to precede the variable & with a symbol, which is the & accessor. For example a , the memory address of the variable is &a .

For a pointer, its value is an address elsewhere, you want to get the value of this address, you can use the * symbol. is * the value of the character. For example, the above &a is an address, then the value stored in this address is *&a .

Thus, & and * is a love to kill brothers, they do the opposite thing.

Beginners of the pointer, often confusing pointer value and pointer address differences, the value of the pointer is an address, is another memory address, the address of the pointer is to store the pointer memory block address. For example, if you have a company key in your home, this key opens the door to your company, and your door needs your own key.

0 values and Nil

Talk is cheaper, let's look at the pointer-related operations in Golang

package mainimport "fmt"func main() {    // 声明一个变量 aVar 类型为 string    var aVar string    fmt.Printf("aVar: %p %#v\n", &aVar, aVar) // 输出 aVar: 0xc42000e240 ""}

We declare a string similar to a variable that has not been assigned, and go will automatically give a value of 0. The 0 value of a character is an empty string. The & memory address of the variable is also read by the symbol.

Fmt. The Printf function can print a variable from a formatted string, indicating that the pointer can be printed, the p v value of the variable can be printed, and # v can print the structure of the variable.

The above procedure can be represented by the following diagram:

         addr        0xc42000e240                    +---------+                    |         |                    |   ""    |                    |         |                    |         |                    +---------+                       aVar

A pointer variable is declared below, and * a pointer variable is declared with a symbol.

    // 声明一个指针变量 aPot 其类型也是 string    var aPot *string    fmt.Printf("aPot: %p %#v\n", &aPot, aPot) // 输出 aPot: 0xc42000c030 (*string)(nil)

The 0 value of the pointer variable is not an empty string, but nil . The value of Apot is a pointer type because the pointer has not yet pointed to another address. It is therefore initialized to nil.

This process can be represented by the following diagram:

         addr        0xc42000c030                    +---------+                    |         |                    |         |                    |  nil    |                    |         |                    +---------+                      aPot

After the normal variable is initialized, you can use the = assignment:

func main() {    // 声明一个变量 aVar 类型为 string    var aVar string    fmt.Printf("aVar: %p %#v\n", &aVar, aVar) // 输出 aVar: 0xc42000e240 ""    aVar = "This is a aVar"    fmt.Printf("aVar: %p %#v\n", &aVar, aVar) // 输出 aVar: 0xc42000e240 "This is a aVar"}

Ordinary variable assignment is very simple, nothing more than a drawer to change a value.

         addr        0xc42000e240                    +---------+                    | This is |                    |         |                    |  a aVar |                    |         |                    +---------+                       aVar

But if a pointer variable with a value of nil, the direct assignment would be problematic.

func main(){    // 声明一个指针变量 aPot 其类型也是 string    var aPot *string    fmt.Printf("aPot: %p %#v\n", &aPot, aPot) // 输出 aPot: 0xc42000c030 (*string)(nil)    *aPot = "This is a Pointer"  // 报错: panic: runtime error: invalid memory address or nil pointer dereference}

The error is also normal, *aPot = "This is a Pointer" meaning that the value of the Apot pointer address is given "This is a Pointer" . However, the value of Apot is nil, but it has not been assigned to an address, so a substring cannot be assigned a value of nil. In addition, even if it is not assigned, the pointer to nil * will be read by the error, after all, not read any address.

The problem is to initialize a memory and assign the memory address to the pointer variable.

    // 声明一个指针变量 aPot 其类型也是 string    var aPot *string    fmt.Printf("aPot: %p %#v\n", &aPot, aPot) // 输出 aPot: 0xc42000c030 (*string)(nil)    aPot = &aVar    *aPot = "This is a Pointer"    fmt.Printf("aVar: %p %#v \n", &aVar, aVar) // 输出 aVar: 0xc42000e240 "This is a Pointer"     fmt.Printf("aPot: %p %#v %#v \n", &aPot, aPot, *aPot) // 输出 aPot: 0xc42000c030 (*string)(0xc42000e240) "This is a Pointer"

We assign the memory address of the Avar to Apot, and we can see that the value of Apot is the address of Avar, but also by reading the value * that the Apot pointer address points to, that is, the value of Avar.

         addr        0xc42000c030             0xc42000c240                    +---------------+        +----------+                    |               |        |          |                    | 0oxc42000x240 |+-----> | This is  |                    |               |        |          |                    |               |        | a aVar   |                    +---------------+        +----------+                      aPot                      aVar

New keyword

With the existing Avar, we can assign a value to the Apot pointer. Yes, if there are no existing variables, go provides new to initialize an address.

    var aNewPot *int    aNewPot = new(int)    *aNewPot = 217    fmt.Printf("aNewPot: %p %#v %#v \n", &aNewPot, aNewPot, *aNewPot) // 输出 aNewPot: 0xc42007a028 (*int)(0xc42006e1f0) 217

New can open up a memory and return the address of the memory. Because the int pointer is a simple type, the operation of New (int), in addition to opening up a memory, can also initialize 0 values, or 0, for this memory.

New does not only open up memory for simple types, but also for compound reference types, although the 0 value of the latter initialization is nil, and if you need to assign a value, there are other problems, which we'll discuss later.

Compound type and nil

Int,string is the underlying type, and array is a composite type based on these underlying types. The pointer initialization of a composite type also needs to be noted:

    var arr [5]int    fmt.Printf("arr: %p %#v \n", &arr, arr) // arr: 0xc420014180 [5]int{0, 0, 0, 0, 0}     arr[0], arr[1] = 1, 2    fmt.Printf("arr: %p %#v \n", &arr, arr) // arr: 0xc420014180 [5]int{1, 2, 0, 0, 0}

Declaring an array of size 5, go automatically initializes the array item to a value of 0, which can be read and assigned by the index.

If an array pointer is declared, that is, the type of a pointer is an array, how does the pointer initialize and assign a value?

    var arrPot *[5]int    fmt.Printf("arrPot: %p %#v \n", &arrPot, arrPot) // arrPot: 0xc42000c040 (*[5]int)(nil)

From the output you can see that the value of Arrpot initialization is nil . We have learned that the value of nil cannot be directly assigned, so the (*arrPot)[0] = 11 direct assignment is wrong.

Function of new key

In this case, we can use new to create a piece of memory and give the memory address to the Arrpot pointer variable. Then the assignment is normal.

    var arrPot *[5]int    fmt.Printf("arrPot: %p %#v \n", &arrPot, arrPot) // arrPot: 0xc42000c040 (*[5]int)(nil)     arrPot = new([5]int)    fmt.Printf("arrPot: %p %#v \n", &arrPot, arrPot) // arrPot: 0xc42000c040 &[5]int{0, 0, 0, 0, 0}     (*arrPot)[0] = 11    fmt.Printf("arrPot: %p %#v \n", &arrPot, arrPot) // arrPot: 0xc42000c040 &[5]int{11, 0, 0, 0, 0}

The above memory graph is as follows:

  addr      0xc42000c040         +------------------+               0xc42000c099 (new创建的内存)         |                  |       +------+------+------+------+------+         |                  |       |      |      |      |      |      |         |    0xc42000c099  +-----> | 11   |  0   |   0  |  0   |  0   |         |                  |       |      |      |      |      |      |         |                  |       +------+------+------+------+------+         +------------------+              arrPot

Reference types and Nil

The go array is a composite type, but not a reference type. The references in Go are similar to Slice,map and so on. Let's look at how the map type initializes the processing of nil.

    var aMap map[string]string    fmt.Printf("aMap: %p %#v \n", &aMap, aMap)  // aMap: 0xc42000c048 map[string]string(nil)

Declares a variable of type map, which is not initialized to a value of 0 after it is declared like an array. Go will initialize the reference type to Nil,nil and cannot be directly assigned. Also, the map and array pointers are different, and you cannot use new to open up a memory and then assign a value. AMAP itself is a value type, the declaration has initialized the memory, but its value is nil, we can not modify the address. &aMap = new(map[string]string)This action will cause an error.

Make keyword

Now that you can't use new, go provides another function make. Make can not only open up a memory, but also give the type of memory to initialize its 0 value, and return this memory instance.

    aMap = make(map[string]string)    aMap["name"] = "Golang"    fmt.Printf("aMap: %p %#v \n", &aMap, aMap) // aMap: 0xc420078038 map[string]string{"name":"Golang"}

New and make

Both new and make are keyword functions that golang the memory used to initialize variables. New returns the address of the memory, and make returns an example of the type when it is returned. For example, new an array, returns an array of memory addresses, make an array, and returns an initialized array.

Through the case above, it is believed that the pointer to the map type can also be initialized with new and make mates.

    var mapPot *map[string]int    fmt.Printf("mapPot: %p %#v \n", &mapPot, mapPot) //  mapPot: 0xc42000c050 (*map[string]int)(nil)     // 初始化map指针的地址    mapPot = new(map[string]int)    fmt.Printf("mapPot: %p %#v \n", &mapPot, mapPot) // mapPot: 0xc42000c050 &map[string]int(nil)      //(*mapPot)["age"] = 21 // 报错    // 初始化map指针指向的map    (*mapPot) = make(map[string]int)    (*mapPot)["age"] = 21    fmt.Printf("mapPot: %p %#v \n", &mapPot, mapPot) // mapPot: 0xc42000c050 &map[string]int{"age":21}

The above code declares a pointer variable mappot, and the type of the pointer variable is a map. Creates a memory for the pointer variable with new and gives it a memory address.
Map is a reference type and its 0 value is nil, so use make to initialize the map, and then the variable can be * assigned to the pointer variable mappot.

In addition to initializing the map, you can initialize the slice and channel, and based on these three types of custom types.

    type User map[string]string    var user User    fmt.Printf("user: %p %#v \n", &user, user)  // user: 0xc42000c060 main.User(nil)    user = make(User)    user["name"] = "Golang"    fmt.Printf("user: %p %#v \n", &user, user) // user: 0xc42007a050 main.User{"name":"Golang"}    var userPot *User    fmt.Printf("userPot: %p %#v \n", &userPot, userPot) // userPot: 0xc42000c068 (*main.User)(nil)    userPot = new(User)    fmt.Printf("userPot: %p %#v \n", &userPot, userPot) // userPot: 0xc42000c068 &main.User(nil)    (*userPot) = make(User)    fmt.Printf("userPot: %p %#v \n", &userPot, userPot) // userPot: 0xc42000c068 &main.User{}    (*userPot)["name"] = "Golang"    fmt.Printf("userPot: %p %#v \n", &userPot, userPot) // userPot: 0xc42000c068 &main.User{"name":"Golang"}

Visible, complex types, as long as the relationship between the pointer and nil is clear, with new and make can easily initialize the data type of Golang.

Pointer in the method

Go allows me to customize the type, and the type can create a method. Like OOP, a method takes an instance object of a type, which is called the recipient, and the recipient can be either an instance variable of the type or an instance pointer variable of the type.

func main(){    person := Person{"vanyar", 21}    fmt.Printf("person<%s:%d>\n", person.name, person.age)    person.sayHi()    person.ModifyAge(210)    person.sayHi()}type Person struct {    name string    age int}func (p Person) sayHi() {    fmt.Printf("SayHi -- This is %s, my age is %d\n",p.name, p.age)}func (p Person) ModifyAge(age int) {    fmt.Printf("ModifyAge")    p.age = age}

The output is as follows:

person<vanyar:21>SayHi -- This is vanyar, my age is 21ModifyAgeSayHi -- This is vanyar, my age is 21

Although the Modifyage method modifies its age field, the p in the method is a copy of the person variable and only modifies the value of the copy. The next time the Sayhi method is called, it is still a copy of the person, so the modification method does not take effect.

One might think that the method would copy the instance variable, if the instance variable is a pointer, wouldn't it be easy to modify it?

    personPot := &Person{"noldor", 27}    fmt.Printf("personPot<%s:%d>\n", personPot.name, personPot.age)    personPot.sayHi()    personPot.ModifyAge(270)    personPot.sayHi()

The output is as follows:

personPot<noldor:27>SayHi -- This is noldor, my age is 27ModifyAgeSayHi -- This is noldor, my age is 27

Visible and no effect, in fact, Go's real copy in Personpot, but will be based on the recipient is a value or pointer type to do an automatic conversion, and then copy the converted object. Which is personPot.ModifyAge(270) actually equivalent to
(*personPot).ModifyAge(270), which is the copy (*personpot). is not related to whether the personpot itself is a value or a pointer.

The real way to modify an object is to set the recipient of the pointer type. The recipient of the pointer type, if the instance object is a value, then go is converted to a pointer and then copied, and if it is a pointer object, copy the pointer instance directly. Because the pointer points to a value, you can naturally modify the object. The code is as follows:

func (p *Person) ChangeAge(age int)  {    fmt.Printf("ModifyAge")    p.age = age}

Go is converted to a pointer type and then copied by person, based on the example type of person. Changeage will become (&person). Changeage.

Summarize

Golang is a concise language that provides pointers for manipulating data memory and modifying variables by reference.

Declaring only unassigned variables, Golang automatically initializes them to 0 values, the 0 values of the underlying data type are simple, the 0 values of the reference type and pointer are not directly assigned to the Nil,nil type, so you need to open up a memory through new, or through make to initialize the data type, or both, Before you can assign a value.

Pointers are also a type, unlike in general, where the value of a pointer is an address, which points to other memory, through which the pointer can read the value stored by the address to which it points.

The recipient of a function method, or a pointer variable. Both the normal recipient and the pointer recipient are copied into the method, different from the copy pointer, which points to the same place, except that its own address is not the same.

The memory address of the text output differs depending on the compilation environment and operation. Reference Code GIST

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.