11 minutes read

In this topic, we will discuss pointers an important subject that can be somewhat difficult for beginners, though. We will try to make it as clear as possible.

What is a pointer?

As you may know, computer memory is made of blocks. Each block has an address and a state. The state represents a value of a variable, whereas the address identifies a block. Thus, every block has its own unique address, represented by a number. In modern computers, we use a 64-bit integer to represent addresses. As you can see, an address itself is nothing more than just an integer value. A variable holding a value of a memory address is called a pointer.

 memory address is a pointer

In high-level languages like Go, we have special control flow statements and functions for controlling execution. However, we still use pointers in a way that we can grant permission to a certain function for it to change an existing variable. Another case when pointers come in handy is when we have a big variable and want to avoid copying it to reduce memory consumption.

Pointer type and a new function

For safety purposes, we work with pointers leading to a specific type:

var addressOfStringVariable *string

In Go, we use an asterisk before the name of the type to declare a variable of the type pointer to a string. In other words, we create a pointer to a string.

To clarify the difference:

  • A variable of the type string is holding a string value

  • A variable of the type *string is holding an address of a variable of the type string

When you declare a pointer, it doesn't share the same default value with the underlying type but instead has its own default value – nil.

var actualStringVariable string       // Is equal to ""
var addressOfStringVariable *string   // Is equal to nil

Now, when we just declare a pointer, it doesn't point anywhere. To create a pointer of some type and actually allocate memory for its default value, we should use a special built-in function new. Let's have a look at how we can use this function:

var p *string   // Declaring p as a pointer to a string
                
p = new(string) // Allocating the memory for the default string value 
                // and assigning its address to the p pointer

This new function returns a pointer to a type, specified as the function argument. We can also merge these two lines into one:

var p = new(string)

Pointer dereference and value assignment

Now, we have a pointer that points to the default value of a given type. Let's check if it's true. The process of getting the actual value from the pointer is called pointer dereference.

var p = new(string)
fmt.Println(p)  // Will print some memory address, for eg. 0xc000040240
                // This address can be different in your case

fmt.Printf("%#v", *p) // Will print the actual value: "" an empty string

We use an asterisk again, but this time with the name of the variable instead. Through this, we dereference the pointer and obtain the actual value. But be careful; dereferencing a nil pointer will lead to a runtime panic.

var p *string
fmt.Println(*p) // invalid memory address or nil pointer dereference

The nil pointer dereference is one of the most common mistakes in Go, so don't forget to check that a pointer is not nil before accessing its value.

It would be boring if we could only have a pointer pointing at the default value. Assigning a value to the variable at the address held by the pointer is not difficult. Follow me in this example:

var p = new(string)
*p = "my string"

Remember, the p variable is nothing more than just a fancy integer representing the memory address. We cannot assign a string to it directly. By putting an asterisk before the variable name, we tell Go that we want to work not with the p variable but with the variable at the address held by the p pointer. In this case, you cannot put an asterisk anywhere you want, only before a valid pointer.

Getting the pointer

One last thing you need to know about pointers is how to get the address of a variable, and it is quite simple:

var s = "some string variable" // var s of the type string
var p = &s                     // var p is a pointer to a string

fmt.Println(s) // Printing the value
fmt.Println(p) // Printing the address where the value is stored

Using the & symbol before the variable name, we tell Go that we work with its address, not its value.

The Go language doesn't allow to move data from one place in memory to another directly, so we can't assign a new address to the variable by calling &varName = address.

Below are proper examples of getting and assigning pointers:

var p *string
var s = "my string"

p = &s
var p = new(string)
var s = "my string"

*p = s

It is important to have the new function in the second example. Otherwise, we would have a nil pointer dereference error at *p = s.

Advanced examples

You will not see questions about the following examples in the practice section. Both of them are about having a pointer that points to another pointer that points to some "solid" type. Sounds complicated, and you'll rarely see such uses in practical application, but you can consider these code snippets and play with them for a bit. It'll definitely help you with the understanding of pointers.

var p **string
p = new(*string)
*p = new(string)
fmt.Println(**p)

**p = "is this even possible?"
fmt.Println(**p)

This is another way:

var s = "yes, it is possible"
var p1 = &s
var p2 = &p1

fmt.Println(*p2 == p1) // Will print true
fmt.Println(**p2 == s) // Will print true

Conclusion

Congrats on making it here! It's been a big topic where we've learned a lot about pointers in Go, what they are and how to use them. In particular, we've covered the following:

  • A pointer is a variable holding the memory address;

  • The type of a pointer is the type it points at;

  • For proper pointer creation, we use the new function;

  • For reference and dereference, you should use the & and * symbols;

  • Finally, for value assignment use *pointer = value.

136 learners liked this piece of theory. 7 didn't like it. What about you?
Report a typo