In this lesson, we’ll explore how pointers work in Go, which is different from languages like Java and .NET. Go gives programmers control over memory allocation and layout, though it doesn’t support pointer arithmetic (e.g., adding to or subtracting from a pointer value). This control helps manage memory more efficiently in systems programming, especially when performance matters.
What Are Pointers?
A pointer in Go holds the memory address of a variable, not the value itself. This is useful when you want to modify the original variable without creating copies, or when you want to optimize memory usage. Let’s start with a basic example:
i := 5
fmt.Println(&i) // prints the memory address of i
Here, &i
returns the memory address of the variable i
.
Declaring Pointers
Once you have a memory address, you can store it in a pointer. Pointers are declared using the *
symbol. Here’s an example of declaring and assigning a pointer to an integer:
var intP *int // Declare a pointer to an int
intP = &i // Assign the address of i to intP
fmt.Println(intP) // Prints the address stored in intP
At this point, intP
holds the address of i
.
Visualizing Pointers
Let’s look at a simple ASCII representation to understand how pointers work in memory:
+---------+ +-------------+
| i | | intP |
+---------+ +-------------+
| 5 | | &i (0x123) |
+---------+ +-------------+
Here, intP
contains the address 0x123
, which points to i
, whose value is 5.
Dereferencing a Pointer
The symbol *
can also be used to dereference a pointer. Dereferencing means accessing the value that the pointer points to. Here’s how it works:
fmt.Println(*intP) // Dereferences intP and prints the value of i (which is 5)
You can think of this as "flattening" the pointer to get the value it points to. Here's a simple rule:
i == *(&i)
This means that the value of i
is equal to the value of the memory address of i
, dereferenced.
Another Example with Dereferencing
Let’s modify the value of a variable using its pointer:
i := 10
var p *int = &i // p points to i
*p = 20 // Change the value at the memory location p points to
fmt.Println(i) // Outputs 20
Changing Values Using Pointers
Here’s a more advanced example where you use a pointer to modify the value of a string:
s := "good bye"
p := &s // p points to s
*p = "ciao" // Change the value s points to
fmt.Println(s) // Outputs: "ciao"
This shows how the value stored at a pointer’s address can be modified, which reflects directly on the original variable.
Memory Representation of Pointers
Here’s how memory looks when using pointers with the example above:
+---------+ +-------------+
| s | | p |
+---------+ +-------------+
| "ciao" | | &s (0x456) |
+---------+ +-------------+
The pointer p
holds the memory address of s
, and dereferencing p
changes the value of s
.
Nil Pointers
A pointer that is not assigned a memory address is a nil pointer. Trying to dereference a nil pointer will cause a runtime error.
var p *int
fmt.Println(*p) // This will crash the program because p is nil
Example of Nil Pointer Dereference (and Crash):
var p *int
*p = 10 // Illegal, will crash
Attempting to dereference a nil pointer (like p
in this case) results in a crash, as no valid memory address has been assigned to it.
Why Use Pointers?
Efficiency: Instead of passing large data structures by value (which creates copies), you can pass pointers, which are much smaller in size (4 bytes on 32-bit systems and 8 bytes on 64-bit systems).
Memory Safety: Unlike C, Go doesn't allow pointer arithmetic, which eliminates a whole class of memory bugs, making Go safer.
one last resource I want to share to understand pointers is this https://www.youtube.com/watch?v=2XEQsJLsLN0 youtube video.
This basic series ends here! This series barely scratched the surface, All the best for your journey from here after! cheers