Pointers are objects whose value is the addresses of other objects (for example, variables).
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:
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
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)
}
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.
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.