Pointers in Golang

Pointers are objects whose value is the addresses of other objects (for example, variables).

Define a Pointer

The pointer is defined as a normal variable, but the data type is preceded by an asterisk *. For example, defining a pointer to an object of type int:

package main

import "fmt"

func main() {
	var t int = 100
	var x *int
	x = &t
	fmt.Println(x)
}

Let's understand what we did here:

  • First, we define a variable (var t int =100).
  • Second, we define a pointer (var x *int).
  • The pointer gets the address of the variable (x = &t).
  • The value of the pointer itself is the address of the variable x (fmt.Println(x)).

Here, the x pointer stores the address of the variable t. Importantly, the variable t is of type int, and the pointer t points specifically to an object of type int. If we try to print the address of the variable to the console, we will see that it represents a hexadecimal value.

In each case, the address may be different, but for example, in my case, the machine address of the variable T is 0xc000012028. That is, in memory, There is an address 0xc000012028 where the variable T is located.

From the address that the pointer stores, we get the value of the variable T. To do this, we use the * or operation Dereference. The result of this operation is the value of the variable that the pointer points to. Let's apply this operation and get the value of the variable t:

package main

import "fmt"

func main() {

	var t int = 100
	var x *int = &t
	
	fmt.Println("Variable Address:", x)
	fmt.Println("Variable Value:", *x)

}

And as a result will get the value and address:

Variable Address: 0xc00011a000
Variable Value: 100

And also using a pointer, we can change the value at the address that is stored in the pointer.

For example, let's define a variable 'a' with the value 10 and change the value using the pointer:

package main

import "fmt"

func main() {

	var a int = 10
	var x *int = &a
	*x = 200
	fmt.Println("the value of the variable a = ", a)
}

The output will be:

the value of the variable a = 200

Nil Value

If a pointer does not have an object address assigned to it, then the pointer defaults to nil (essentially no value). If we try to get a value from such an empty pointer, we will encounter an error:

package main

import "fmt"

func main() {

	var f *int
	fmt.Println("Value:", *f)
}

As an output will be an error like this:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x47aa54]

Therefore, when working with pointers, it is sometimes advisable to check for the value of nil:

var f *int
	if f != nil{
	fmt.Println("Value:", *f)
	}

Function new()

A variable represents a named object in memory. The Go language also allows you to create unnamed objects that are also placed in memory, but do not have a name as variables. To do this, use the new(type) function. The type whose object you want to create is passed to this function. The function returns a pointer to the created object:

package main

import "fmt"

func main() {

	a := new(int)
	fmt.Println("Value:", *a) // Here the a variable is = 0
	*t = 100                  // We change the value to 100
	fmt.Println("Value:", *t) // Value here equals to 100
}

In this case, the pointer t will be of type(*int), because it points to an object of type int. The object to be created has a default value (for type int it's the number 0).

An object created with the new function is no different from a regular variable. The only thing to access this object is to get or change its address, you need to use a pointer.

Pointers with Functions

By default, all parameters are passed to the function by value. For example:

package main
import "fmt"
 
func square(a int){
    a = a * a
}
func main() {
     
    b := 100
    fmt.Println("the value of b before:", b)     
    square(b)                 
    fmt.Println("the value of b after:", b)     
}

The output:

the value of b before: 100
the value of b after: 100

The square function changes the value of a parameter, squaring it. But after calling this function, we can see that the value of the variable b that is passed to square has not changed. After all, the function receives a copy of this variable and works with it independently of the original variable b.

However, what if we do need to change the value of the variable being passed? In this case, we can use pointers:

package main
import "fmt"
 
func square(a *int){
    *a = (*a) * (*a)
}
func main() {
     
    b := 100
    fmt.Println("the value of b before:", b)     
    square(&b)                 
    fmt.Println("the value of b after:", b)     
}

And the result will be like this:

the value of b before: 100
the value of b after: 10000

The square function now accepts a pointer to an object of type int as a parameter. When the square function is called, the address of the variable is passed to it b (changeValue(&b)). And after it is executed, we see that the value of the variable d has changed.