Golang Tutorial
Fundamentals
Control Statements
Functions & Methods
Structure
Arrays & Slices
String
Pointers
Interfaces
Concurrency
In Go, just as you can have pointers pointing to variables, you can also have pointers that point to other pointers, commonly known as double pointers or pointer-to-pointer. Using double pointers might be less common than regular pointers, but they can be essential in specific scenarios, especially when working with C libraries using cgo or when manipulating data structures.
Let's dive into the concept with some details and examples.
Before delving into double pointers, it's essential to have a grasp of what a regular pointer is. In Go, a pointer holds the memory address of a value. The type *T
is a pointer to a T
value.
var x int = 10 var p *int = &x
Here, p
is a pointer to an int
. It holds the address of the variable x
.
A pointer to a pointer is essentially a variable which holds the address of another pointer variable. If we take the previous example, to create a pointer to p
, you'd declare a pointer of type **int
.
var pp **int = &p
Here, pp
is a pointer to a pointer to an int
. It holds the address of the pointer variable p
.
Here's how you can work with double pointers:
Dereferencing: To get the value that pp
points to (which is a pointer), you'd use *pp
. To get the value that the pointer pp
points to points to (i.e., the original value x
), you'd use **pp
.
Assignment: You can change the value of x
through pp
using:
**pp = 20
Let's consolidate this understanding with an example:
package main import "fmt" func main() { // Declare an integer and a pointer to the integer x := 10 p := &x // Declare a pointer to the pointer pp := &p // Display the values and addresses fmt.Println("Value of x:", x) fmt.Println("Address of x:", &x) fmt.Println("Value of p (Address of x):", p) fmt.Println("Dereferencing p to get the value of x:", *p) fmt.Println("Address of p:", &p) fmt.Println("Value of pp (Address of p):", pp) fmt.Println("Dereferencing pp to get the value of p:", *pp) fmt.Println("Double Dereferencing pp to get the value of x:", **pp) // Modifying x through pp **pp = 20 fmt.Println("Value of x after modifying through pp:", x) }
Dynamic Data Structures: In some data structures, especially trees and graphs, double pointers can be handy in scenarios where you want to modify the pointer itself.
Working with C Libraries: When using cgo to interact with C libraries, you might encounter situations where a C function expects a pointer to a pointer.
Double pointers in Go are pointers that point to other pointers. Though their usage might seem confusing at first, understanding the concept and the need for them in specific scenarios is crucial for certain advanced programming tasks.
Double Pointer Usage in Golang:
package main import "fmt" func main() { var num int = 42 var ptr *int = &num var doublePtr **int = &ptr fmt.Println("Value:", **doublePtr) // Output: 42 }
Working with Pointer to Pointer in Golang:
package main import "fmt" func main() { var num int = 42 var ptr *int = &num var doublePtr **int = &ptr fmt.Println("Value through double pointer:", **doublePtr) // Output: 42 }
Pointer Indirection and Double Pointers in Golang:
package main import "fmt" func main() { var num int = 42 var ptr *int = &num var doublePtr **int = &ptr fmt.Println("Indirect access through double pointer:", ***doublePtr) // Output: 42 }
Passing Pointer to Pointer as a Function Argument in Golang:
package main import "fmt" func main() { var num int = 42 var ptr *int = &num modifyPointerValue(&ptr) fmt.Println("Modified value through pointer:", *ptr) // Output: 100 } func modifyPointerValue(doublePtr **int) { ***doublePtr = 100 }
Dynamic Memory Allocation with Double Pointers in Golang:
package main import "fmt" func main() { var doublePtr **int allocateMemory(&doublePtr) **doublePtr = 50 fmt.Println("Value from dynamically allocated memory:", **doublePtr) // Output: 50 } func allocateMemory(doublePtr ***int) { temp := 42 *doublePtr = &temp }
Pointer Arithmetic with Double Pointers in Golang:
package main import "fmt" func main() { var num int = 42 var ptr *int = &num var doublePtr **int = &ptr // Pointer arithmetic simulation temp := uintptr(unsafe.Pointer(*doublePtr)) + unsafe.Sizeof(int(0)) *doublePtr = (*int)(unsafe.Pointer(temp)) fmt.Println("Value after pointer arithmetic:", **doublePtr) // Output: 0 (Simulated increment) }
Common Use Cases for Double Pointers in Golang:
package main import "fmt" func main() { var num int = 42 var ptr *int = &num var doublePtr **int = &ptr // Common use case: Dynamic memory allocation allocateMemory(&doublePtr) **doublePtr = 100 fmt.Println("Value from dynamically allocated memory:", **doublePtr) // Output: 100 } func allocateMemory(doublePtr ***int) { temp := 0 *doublePtr = &temp }