You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A variable that holds the memory address of where in the memory an associated value is stored
Variables are stored in one or more contiguous memory addresses
Different variable types can take different number of memory addresses
Depends on the size of the data type
The smallest amount of addressable memory is a byte (8-bits)
varxint32=10// 4 bytesvarybool=true// 1 byte
A pointer is a variable that contains the memory address of another variable
Holds a number that indicates the memory location where the data is stored
This number is the Memory Address
// Pointer Operators// -----------------fmt.Println("Pointer Operators:")
fmt.Println("------------------")
varxint32=10// Value-type int32varybool=true// Value-type boolvarptrX*int32=&x// Pointer-type to a value of type int32ptrY:=&y// Pointer-type to a value of type boolfmt.Println("ptrX =", ptrX) // Prints the memory addressfmt.Println("*ptrX =", *ptrX) // Prints the pointed value: Same as xfmt.Println("x =", x)
fmt.Println("ptrY =", ptrY) // Prints the memory addressfmt.Println("*ptrY =", *ptrY) // Prints the pointed value: Same as yfmt.Println("y =", y)
Every pointer is always occupying the same (fixed) number of memory locations
Regardless of the type it is pointing to
In the example, we are using 4 bytes memory-address length
In modern computer, it is usually 8 bytes memory-address length
In Go, the size of a pointer variable is:
8-bytes for 64-bit machines
4-bytes for 32-bit machines
Zero-Value of a Pointer: nil
nil is untyped identifier
Represents lack of value
Not another name for 0 unlike in C
Cannot convert back and forth with a number
Defined in the Universe block
Can be shadowed
Never name variables nil
WARNING: Before dereferencing a pointer, make sure that it is not nil
Attempting to dereference a nil pointer results in a panic
// Example of nil Pointer// ----------------------fmt.Println("Example of nil Pointer:")
fmt.Println("-----------------------")
varptrZ*string// Pointer-type to a value of type string but default to nilfmt.Println("ptrZ =", ptrZ) // Prints nil// Attempting to dereference a nil pointer results in a panic// Invalid Memory Address & Segmentation Violation// fmt.Println("*ptrZ =", *ptrZ) // panic: runtime error: invalid memory address or nil pointer dereference
Slice, Map, and Function are implemented using Pointers
Which is why their zero-values are the same: nil
Channel and Interface are also implemented using Pointers
Go's pointer syntax is partially borrowed from C/C++
But without painful memory management
Go is a Garbage-Collected language
Some pointer features in C/C++ are not allowed (E.g. Pointer Arithmetics)
NOTE: Go has unsafe package for low-level operations
But it is exceedingly rare to use unsafe
& - Address Operator
Precedes a value-type variable
Returns the memory address where the value of that variable is stored
This is called Referencing the variable
* - Indirection Operator
Precedes a pointer-type variable
Returns the pointed value at that memory address
This is called Dereferencing the pointer
However, when used on a type instead of a variable, it denotes a Pointer-Type to that type
WARNING: Before dereferencing a pointer, make sure that it is not nil
Attempting to dereference a nil pointer results in a panic
// Pointer Operators// -----------------varxint32=10// Value-type int32varptrX*int32=&x// Pointer-type to a value of type int32: Referencingfmt.Println("ptrX =", ptrX) // Prints the memory address: 0xc000012128fmt.Println("*ptrX =", *ptrX) // Dereferencing: Prints the pointed value: 10varnilPtr*string// Pointer-type to a value of type string but default to nilfmt.Println("nilPtr =", nilPtr) // Prints nilfmt.Println(nilPtr==nil) // Prints true// fmt.Println("*nilPtr =", *nilPtr) // panic: runtime error: invalid memory address or nil pointer dereference
Pointer Type
A type that represents a pointer
Written with a * before the type name (E.g. *int)
Can be based on any type
// Example of Pointer Type// -----------------------fmt.Println("Example of Pointer Type:")
fmt.Println("------------------------")
intVal:=10varptrIntVal*intptrIntVal=&intValfmt.Println("intVal =", intVal)
fmt.Println("ptrIntVal =", ptrIntVal)
Built-in function new() creates a pointer variable
Returns a pointer to a zero-value instance of the type
Allows to not set the pointer to nil
But new() is rarely used
// Example of Using new()// ----------------------fmt.Println("Example of Using new():")
fmt.Println("-----------------------")
ptrNewVar:=new(int) // Returns a pointer to 0fmt.Println("ptrNewVar == nil:", ptrNewVar==nil) // falsefmt.Println("*ptrNewVar =", *ptrNewVar) // 0
For Struct, use & before the Struct literal
Cannot use & on primitive literals or constants
They do not have memory address
Exist only at compile time
If pointer is needed for them, declare a variable instead
Not being able to get the address of a constant is sometimes inconvenient
Cannot assign literals directly to pointer-type fields
// Unable to Get Address of Constants// ----------------------------------typePersonstruct {
FirstNamestringMiddleName*stringLastNamestring
}
p:=Person{
FirstName: "John",
MiddleName: "Edler", // cannot use "Edler" (type string) as type *string in field value.LastName: "Smith",
}
p:=Person{
FirstName: "John",
MiddleName: &"Edler", // cannot take the address of "Edler".LastName: "Smith",
}
2 ways to solve this:
1. Introduce a variable to hold the constant value
2. Write a generic helper function: Takes a param of any type and return a pointer to that type
With the generic approach
The constant is copied to the generic function as variable (param)
Variables have memory address
// Generic Pointer Helper For Constants// ------------------------------------// Generic helper function for getting constant's pointer.funcmakeConstPtr[Tany](tT) *T {
return&t
}
funcmain() {
// Generic Pointer Helper For Constants// ------------------------------------fmt.Println("Generic Pointer Helper For Constants:")
fmt.Println("-------------------------------------")
typePersonstruct {
FirstNamestringMiddleName*stringLastNamestring
}
p:=Person{
FirstName: "John",
MiddleName: makeConstPtr("Edler"), // This works!LastName: "Smith",
}
fmt.Println("p =", p)
}
Pointers Behavior Like Classes
Pointers might look intimidating
But Pointers are actually the familiar behavior for classes in other languages
In other languages, there is a behavior difference between primitives and classes
When primitives are aliased or passed to functions, changes made to the other variable/parameter are not reflected
The aliases (params vs args) do not share the same memory
They are often referred to as Passed-By-Value in Java and JavaScript
Python and Ruby use Immutable Instances for the same purpose
# Python As An Example: Immutable Instance# ----------------------------------------x=10y=x# Attempt aliasingy=20print(x) # Prints 10defattempt_change(a):
a=1000attempt_change(x)
print(x) # Prints 10
This is not the case when an instance of a class is done the same
Change in one variable also affect the other
# Python As An Example: Mutable Instance# --------------------------------------classFoo:
def__init__(self, x):
self.x=xdefinner1(f):
f.x=20definner2(f):
f=Foo(30) # New instance: Local scopedefouter():
f=Foo(10)
inner1(f)
print(f.x) # Prints 20: f.x assigned in inner1()inner2(f)
print(f.x) # Prints 20: f in inner2() is new local-scoped instance. Outside f was shadowed.g=Noneinner2(g)
print(gisNone) # Prints True: f in inner2() is new local-scoped instance. Outside g was shadowed.outer()
# 20# 20# True
The following scenario is true in other languages
Pass an instance of a class to a function and change the value of a field
The change is reflected in the variable that was passed in
Reassign the parameter in the function
The change is not reflected in the variable that was passed in
Pass nil/null/None for a parameter value: Setting the parameter itself to a new value
Does not modify the variable in the calling function
This is often explained that in other languages, class instances are Passed-By-Reference
But that is not true
Else, scenario 2 and 3 above would affect the variable
They are always Pass-By-Value, just as in Go
However, every instance of a class in these languages are implemented as Pointer
When class instance passed to a function => The copied passed value is the Pointer
Referring to the same memory address => Changes made to one is reflected to the other (E.g. f above is a pointer)
Re-assigning a new instance creates a separate instance/local variable (separate memory address)
The same behavior applies when using Pointer Variables in Go
But Go gives the choice to use pointers or values for both primitives and Structs
Most of the time, use values
Make it easier to understand how and when the data is modified
Also reduces the work of the Garbage Collector
Pointers Indicate Mutable Parameters
Go constants provide names for literal expressions that can be calculated at compile time
Go has no mechanism to declare immutability
But immutability is a good thing
Safer from bugs
Easier to understand
More ready for change
Ability to choose between Value and Pointer addresses mutability/immutability
Using Pointer == The variable is mutable
Not using Pointer == The variable is not mutable
Go is a Call-By-Value language
Values passed to functions are copies
For non-pointer-types, called functions cannot modify original arguments
The original data's immutability is guaranteed
When passing pointers to function, the original data can be modified
This is because the pointer itself is passed-by-value
When passing nil pointers, cannot make the value non-nil
Can only reassign if a value was already assigned to the pointer
nil is a fixed location in-memory
Also, we cannot change the pointer as it is passed-by-value to the function
If we want the value assigned to a pointer parameter to last after exiting the function, dereference the pointer and set the value
Dereferencing allows to access the value pointed by the pointer
Also allows to set the value pointed by the pointer
Attempting to change value at an address by re-assiging a new address to a pointer will not work
We cannot change the pointer as it is Passed-By-Value to the function
We can only change a pointed value by dereferencing the pointer, then re-assign a value
// Example of Dereferencing a Pointer to Update Pointed Value// ----------------------------------------------------------// A function that does not dereference the parameter fails to update.funcfailsToUpdate(ptrX*int) {
// Attempting to change value at an address by re-assiging a new address to a pointer// Address to address// Does not work because ptrX was Passed-By-ValuenewValue:=20ptrX=&newValue
}
// A function that dereference the parameter succeed to update.funcsucceedToUpdate(ptrX*int) {
// Change a pointed value by dereferencing the pointer, then re-assign a value// Value to valuenewValue:=20*ptrX=newValue
}
funcmain() {
// Example of Dereferencing a Pointer to Update Pointed Value// ----------------------------------------------------------fmt.Println("Example of Dereferencing a Pointer to Update Pointed Value:")
fmt.Println("-----------------------------------------------------------")
someInt:=100fmt.Println("someInt =", someInt)
failsToUpdate(&someInt)
fmt.Println("After failsToUpdate(&someInt), someInt =", someInt)
succeedToUpdate(&someInt)
fmt.Println("After succeedToUpdate(&someInt), someInt =", someInt)
fmt.Println()
}
Pointer: Last Resort
Be careful when using pointers in Go
Can make it hard to understand data flow
Can create extra-work for the Garbage Collector
E.g. Prefer instantiating a Struct instead of modifying a Struct
// Don't do thisfuncMakeFoo(f*Foo) error {
f.Field1="val"f.Field2=20returnnil
}
// Do this insteadfuncMakeFoo() (Foo, error) {
f:=Foo{
Field1: "val",
Field2: 20,
}
returnf, nil
}
When the function expects an interface, use pointer parameters