05. Pointers


Whenever a memory is declared a certain amount of memory in the RAM is allocated for it and it is based on the data type of the variable being used in the program. This memory allocation done, while the program is running and hence a variable might get a different address every time a program is run.
A Pointer is a variable that holds memory address of another variable.
A pointer do not only holds the memory addresses but they also point where the memory is allocated and provide ways to find or even change the value located at that memory location.
Address and Dereference operators
& operator: The address of a variable can be obtained by preceding the name of a variable with an ampersand sign (&), known as address-of operator.
operator:\ It is known as the **dereference operator. When placed before an address**\, it returns the value at that address.*
package main
import "fmt"
func main() {
i := 10
fmt.Printf("%T %v \n", &i, &i)
fmt.Printf("%T %v \n", *(&i), *(&i))
}
Output:
The output will display the type and value of the pointer to i
, followed by the type and value of the dereferenced pointer:
*int 0xc0000120a8
int 10
Note: The memory address (0xc0000120a8
) will vary each time you run the program.
Declaring and initializing pointers
Declaring pointer
Syntax
var <pointer_name> *<data_type>
- Asterisk (*) over here used to declare a pointer. So, do not confuse it with the dereference operator.
Example:
package main
import "fmt"
func main() {
var i *int
var s *string
fmt.Println(i)
fmt.Println(s)
}
Output:
The output will display the zero values of the pointers, which is nil
for both:
<nil>
<nil>
Initializing pointer
Syntax
var <pointer_name> *<data_type> = &<variable_name>
var <pointer_name> = &<variable_name>
- In this case, the datatype will be internally determined by the compiler.
<pointer_name> := &<variable_name>
package main
import "fmt"
func main() {
s := "hello"
var a *string = &s
fmt.Println(a)
var b = &s
fmt.Println(b)
c := &s
fmt.Println(c)
}
Output:
The output will display the memory addresses of the string s
for each pointer a
, b
, and c
. The actual memory addresses will vary each time you run the program, but they will all point to the same location since they reference the same variable s
. The output will look something like this:
0xc000010220
0xc000010220
0xc000010220
Dereferencing a pointer
We have already studied about the dereference operator. When a dereference operator is placed before an address, it gives us the value of that particular address. But since, a pointer is also a variable, that stores memory address of some other variable, we can use the dereference operator with the pointer as well.
To get the value of the variable whose memory address we are storing in a pointer, we can simply write asterisk which is our dereference operator and then the name of the pointer.
We can even modify the value by using the dereference operator by simply assigning it with a new value.
package main
import "fmt"
func main() {
s := "hello"
fmt.Printf("%T : %v \n", s, s) // Output: string : hello
ps := &s
*ps = "hello world"
fmt.Printf("%T : %v \n", s, s) // Output: string : hello world
}
Output:
string : hello
string : hello world
The output shows the type and initial value of s
, followed by the type and updated value after modifying it through the pointer ps
.
There are two ways of passing arguments to a function
- Passing by value, 2. Passing by reference.
Passing by value in function
Function is called by directly passing the value of the variable as an argument.
The parameter is copied into another location of the memory.
So, when accessing or modifying the variable within the function, only the copied is accessible or modified, and the original value is never modified.
All basic types (int, float, bool, string, array) are passed by value.
Example 1.
package main
import "fmt"
func modify(a int) {
a += 100
}
func main() {
a := 10
fmt.Println(a) // Output: 10
modify(a)
fmt.Println(a) // Output: 10
}
Output:
10
10
The output shows that the value of a
remains 10
before and after the modify
function call, demonstrating that the original variable is not changed due to pass-by-value.
Example 2.
package main
import "fmt"
func modify(s string) {
s = "world"
}
func main() {
a := "hello"
fmt.Println(a) // Output: hello
modify(a)
fmt.Println(a) // Output: hello
}
Output:
hello
hello
The output shows that the value of a
remains "hello"
before and after the modify
function call, demonstrating that the original variable is not changed due to pass-by-value.
Passing by reference in function
Go supports pointers, allowing us to pass references to value within the programIn.
In call by reference/pointer, the address of the variable is passed into the function call as the actual parameter.
All the operations in the function are performed on the value stored at the address of the actual parameters.
Example 1.
package main
import "fmt"
func modify(s *string) {
*s = "world"
}
func main() {
a := "hello"
fmt.Println(a) // Output: hello
modify(&a)
fmt.Println(a) // Output: world
}
Output:
hello
world
The output shows that the value of a
changes from "hello"
to "world"
after the modify
function call, demonstrating that the original variable is modified due to pass-by-reference using pointers.
Slices and maps are passed by reference, by default.
Example 2. Slice
package main
import "fmt"
func modify(s []int) {
s[0] = 100
}
func main() {
slice := []int{10, 20, 30}
fmt.Println(slice) // Output: [10 20 30]
modify(slice)
fmt.Println(slice) // Output: [100 20 30]
}
Output:
[10 20 30]
[100 20 30]
The output shows that the first element of slice
changes from 10
to 100
after the modify
function call, demonstrating that slices are passed by reference, allowing the original slice to be modified.
Example 3. Maps
package main
import "fmt"
func modify(m map[string]int) {
m["K"] = 75
}
func main() {
ascii_codes := make(map[string]int)
ascii_codes["A"] = 65
ascii_codes["B"] = 70
fmt.Println(ascii_codes) // Output: map[A:65 B:70]
modify(ascii_codes)
fmt.Println(ascii_codes) // Output: map[A:65 B:70 K:75]
}
Output:
map[A:65 B:70]
map[A:65 B:70 K:75]
The output shows that the map ascii_codes
is modified by adding a new key-value pair "K": 75
after the modify
function call, demonstrating that maps are passed by reference, allowing the original map to be modified.
References
Subscribe to my newsletter
Read articles from Arindam Baidya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
