Skip to content

Commit e28a6a0

Browse files
andrerfcsantosiHiDErikSchierboom
committed
Add pointer concept
Co-authored-by: Jeremy Walker <[email protected]> Co-authored-by: Erik Schierboom <[email protected]>
1 parent 131ca49 commit e28a6a0

File tree

15 files changed

+972
-6
lines changed

15 files changed

+972
-6
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"blurb": "TODO: add blurb",
3-
"authors": ["ErikSchierboom"],
2+
"blurb": "Pointers hold the memory address of a value.",
3+
"authors": ["andrerfcsantos"],
44
"contributors": []
55
}

concepts/pointers/about.md

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,154 @@
11
# About
22

3-
TODO: add information on pointers concept
3+
Like many other languages, Go has pointers.
4+
If you're new to pointers, they can feel a little mysterious but once you get used to them, they're quite straight-forward.
5+
They're a crucial part of Go, so take some time to really understand them.
6+
7+
Before digging into the details, it's worth understanding the use of pointers. Pointers are a way to share memory with other parts of our program, which is useful for two major reasons:
8+
1. When we have large amounts of data, making copies to pass between functions is very inefficient.
9+
By passing the memory location of where the data is stored instead, we can dramatically reduce the resource-footprint of our programs.
10+
2. By passing pointers between functions, we can access and modify the computer's memory directly, meaning that any changes made by one function are immediately visible to other parts of the program when the function ends.
11+
12+
## Variables and Memory
13+
14+
Let's say we have a regular integer variable `a`:
15+
16+
```go
17+
var a int
18+
```
19+
20+
When we declare a variable, Go has to find a place in memory to store its value. This is largely abstracted from us — when we need to fetch the value stored in that piece of memory, we can just refer to it by the variable name.
21+
22+
For instance, when we write `a + 2`, we are effectively fetching the value stored in the memory associated with the variable `a` and adding 2 to it.
23+
24+
Similarly, when we need to change the value in the piece of memory of `a`, we can use the variable name to do an assignment:
25+
26+
```go
27+
a = 3
28+
```
29+
30+
The piece of memory that is associated with `a` will now will now be storing the value `3`.
31+
32+
## Pointers
33+
34+
While variables allow us to refer to values in memory, sometimes it's useful to know the **memory address** to which the variable is pointing. **Pointers** hold the memory addresses of those values. A pointer declaration looks like this:
35+
36+
```go
37+
var p *int // 'p' contains the memory address of an integer
38+
```
39+
40+
Here we declaring a variable `p` of type `*int`. The type `*int` means "pointer to int". In other words, we are saying that `p` is a pointer to int, which means that `p` will hold the memory address of an integer. The zero value of pointers is `nil` because a `nil` pointer holds no memory address.
41+
42+
### Getting a pointer to a variable
43+
44+
To find the memory address of the value of a variable, we can use the `&` operator.
45+
For example, if we want to find and store the member address of variable `a` in the pointer `p`, we can do the following:
46+
47+
```go
48+
var a int
49+
a = 2
50+
51+
var p *int
52+
p = &a // the variable 'p' will hold the memory address of 'a'
53+
```
54+
55+
### Accessing the value via a pointer (dereferencing)
56+
57+
When we have a pointer, we might want to know the value stored in the memory address to which it points. We can do this using the `*` operator:
58+
59+
```go
60+
var a int
61+
a = 2
62+
63+
var p *int
64+
p = &a // the variable 'p' will hold the memory address of 'a'
65+
66+
var b int
67+
b = *p // b == 2
68+
```
69+
70+
The operation `*p` fetches the value stored at the memory address stored in `p`. This operation is often called "dereferencing".
71+
72+
We can also use the derefering operator to assign a new value to the memory address referenced by the pointer:
73+
74+
```go
75+
var a int
76+
a = 2 // declare int variable 'a' and assign it the value of 2
77+
78+
var pa *int
79+
pa = &a // 'pa' now refers to the memory address of 'a'
80+
*pa = *pa + 2 // increment by 2 the value at memory address 'pa'
81+
82+
fmt.Println(a) // Output: 4
83+
// 'a' will have the new value that was changed via the pointer!
84+
```
85+
86+
Assigning to `*pa` will change the value stored at the memory address `pa` holds. Since `pa` holds the memory address of `a`, by assigning to `*pa` we are effectively changing the value of `a`!
87+
88+
A note of caution however: always check if a pointer is not `nil` before dereferencing. Dereferencing a `nil` pointer will make the program crash at runtime!
89+
90+
### Pointers to structs
91+
92+
So far we've only seen pointers to primitive values. We can also create pointers for structs:
93+
94+
```go
95+
type Person struct {
96+
Name string
97+
Age int
98+
}
99+
100+
var peter Person
101+
peter = Person{Name: "Peter", Age: 22}
102+
103+
var p *Person
104+
p = &peter
105+
```
106+
107+
We could have also created a new `Person` and immediately stored a pointer to it:
108+
109+
```go
110+
var p *Person
111+
p = &Person{Name: "Peter", Age: 22}
112+
```
113+
114+
When we have a pointer to a struct, we don't need to dereference the pointer before accessing one of the fields:
115+
116+
```go
117+
var p *Person
118+
p = &Person{Name: "Peter", Age: 22}
119+
120+
fmt.Println(p.Name) // Output: "Peter"
121+
// Go automatically dereferences 'person' to allow
122+
// access to the 'Name' field
123+
```
124+
125+
## Slices and maps are already pointers
126+
127+
Slices and maps are special types because they already have pointers in their implementation. This means that more often that not, we don't need to create pointers for these types to share the memory address for their values. Imagine we have a function that adds a key to a map:
128+
129+
130+
```go
131+
func addPeterAge(m map[string]int){
132+
m["Peter"] = 22
133+
}
134+
```
135+
136+
If we create a map and call this function, the changes the function made to the map persist after the function ended. This is a similar behavior we get if we were using a pointer, but note how on this example we are not using any referencing/dereferencing or any of the pointer syntax:
137+
138+
```go
139+
ages := make(map[string]int)
140+
addPeterAge(ages)
141+
fmt.Println(ages)
142+
// Output: map[Peter:22]
143+
// The changes the function 'addPeterAge' made to the map are visible after the function ends!
144+
```
145+
146+
## Pointer arithmetic
147+
148+
Unlike other languages, pointer arithmetic is not allowed in Go. This means that snippets like the following are invalid:
149+
150+
```go
151+
a := 2
152+
pa := &a
153+
pa++ // NOT ALLOWED: incrementing the pointer or any other pointer arithmetic operations are invalid
154+
```

concepts/pointers/introduction.md

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,144 @@
1-
# Introduction
1+
# About
22

3-
TODO: add introduction for pointers concept
3+
Like many other languages, Go has pointers.
4+
If you're new to pointers, they can feel a little mysterious but once you get used to them, they're quite straight-forward.
5+
They're a crucial part of Go, so take some time to really understand them.
6+
7+
Before digging into the details, it's worth understanding the use of pointers. Pointers are a way to share memory with other parts of our program, which is useful for two major reasons:
8+
1. When we have large amounts of data, making copies to pass between functions is very inefficient.
9+
By passing the memory location of where the data is stored instead, we can dramatically reduce the resource-footprint of our programs.
10+
2. By passing pointers between functions, we can access and modify the computer's memory directly, meaning that any changes made by one function are immediately visible to other parts of the program when the function ends.
11+
12+
## Variables and Memory
13+
14+
Let's say we have a regular integer variable `a`:
15+
16+
```go
17+
var a int
18+
```
19+
20+
When we declare a variable, Go has to find a place in memory to store its value. This is largely abstracted from us — when we need to fetch the value stored in that piece of memory, we can just refer to it by the variable name.
21+
22+
For instance, when we write `a + 2`, we are effectively fetching the value stored in the memory associated with the variable `a` and adding 2 to it.
23+
24+
Similarly, when we need to change the value in the piece of memory of `a`, we can use the variable name to do an assignment:
25+
26+
```go
27+
a = 3
28+
```
29+
30+
The piece of memory that is associated with `a` will now will now be storing the value `3`.
31+
32+
## Pointers
33+
34+
While variables allow us to refer to values in memory, sometimes it's useful to know the **memory address** to which the variable is pointing. **Pointers** hold the memory addresses of those values. A pointer declaration looks like this:
35+
36+
```go
37+
var p *int // 'p' can hold the memory address of an integer
38+
```
39+
40+
Here we declaring a variable `p` of type `*int`. The type `*int` means "pointer to int". In other words, we are saying that `p` is a pointer to int, which means that `p` will hold the memory address of an integer. The zero value of pointers is `nil` because a `nil` pointer holds no memory address.
41+
42+
### Getting a pointer to a variable
43+
44+
To find the memory address of the value of a variable, we can use the `&` operator.
45+
For example, if we want to find and store the member address of variable `a` in the pointer `p`, we can do the following:
46+
47+
```go
48+
var a int
49+
a = 2
50+
51+
var p *int
52+
p = &a // the variable 'p' will hold the memory address of 'a'
53+
```
54+
55+
### Accessing the value via a pointer (dereferencing)
56+
57+
When we have a pointer, we might want to know the value stored in the memory address to which it points. We can do this using the `*` operator:
58+
59+
```go
60+
var a int
61+
a = 2
62+
63+
var p *int
64+
p = &a // the variable 'p' will hold the memory address of 'a'
65+
66+
var b int
67+
b = *p // b == 2
68+
```
69+
70+
The operation `*p` fetches the value stored at the memory address stored in `p`. This operation is often called "dereferencing".
71+
72+
We can also use the derefering operator to assign a new value to the memory address referenced by the pointer:
73+
74+
```go
75+
var a int
76+
a = 2 // declare int variable 'a' and assign it the value of 2
77+
78+
var pa *int
79+
pa = &a // 'pa' now refers to the memory address of 'a'
80+
*pa = *pa + 2 // increment by 2 the value at memory address 'pa'
81+
82+
fmt.Println(a) // Output: 4
83+
// 'a' will have the new value that was changed via the pointer!
84+
```
85+
86+
Assigning to `*pa` will change the value stored at the memory address `pa` holds. Since `pa` holds the memory address of `a`, by assigning to `*pa` we are effectively changing the value of `a`!
87+
88+
A note of caution however: always check if a pointer is not `nil` before dereferencing. Dereferencing a `nil` pointer will make the program crash at runtime!
89+
90+
### Pointers to structs
91+
92+
So far we've only seen pointers to primitive values. We can also create pointers for structs:
93+
94+
```go
95+
type Person struct {
96+
Name string
97+
Age int
98+
}
99+
100+
var peter Person
101+
peter = Person{Name: "Peter", Age: 22}
102+
103+
var p *Person
104+
p = &peter
105+
```
106+
107+
We could have also created a new `Person` and immediately stored a pointer to it:
108+
109+
```go
110+
var p *Person
111+
p = &Person{Name: "Peter", Age: 22}
112+
```
113+
114+
When we have a pointer to a struct, we don't need to dereference the pointer before accessing one of the fields:
115+
116+
```go
117+
var p *Person
118+
p = &Person{Name: "Peter", Age: 22}
119+
120+
fmt.Println(p.Name) // Output: "Peter"
121+
// Go automatically dereferences 'person' to allow
122+
// access to the 'Name' field
123+
```
124+
125+
## Slices and maps are already pointers
126+
127+
Slices and maps are special types because they already have pointers in their implementation. This means that more often that not, we don't need to create pointers for these types to share the memory address for their values. Image we have a function that adds a key to a map:
128+
129+
130+
```go
131+
func addPeterAge(m map[string]int){
132+
m["Peter"] = 22
133+
}
134+
```
135+
136+
If we create a map and call this function, the changes the function made to the map persist after the function ended. This is a similar behavior we get if we were using a pointer, but note how on this example we are not using any referencing/dereferencing or any of the pointer syntax:
137+
138+
```go
139+
ages := make(map[string]int)
140+
addPeterAge(ages)
141+
fmt.Println(ages)
142+
// Output: map[Peter:22]
143+
// The changes the function 'addPeterAge' made to the map are visible after the function ends!
144+
```

concepts/pointers/links.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
1-
[]
1+
[
2+
{
3+
"url": "https://tour.golang.org/moretypes/1",
4+
"description": "Tour of Go: Pointers"
5+
},
6+
{
7+
"url": "https://gobyexample.com/pointers",
8+
"description": "Go by Example: Pointers"
9+
}
10+
]

config.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,23 @@
282282
"basics"
283283
],
284284
"status": "beta"
285+
},
286+
{
287+
"name": "Election Day",
288+
"slug": "election-day",
289+
"uuid": "b9f43f40-2540-48be-b721-71117dee2250",
290+
"concepts": [
291+
"pointers"
292+
],
293+
"prerequisites": [
294+
"structs",
295+
"slices",
296+
"maps",
297+
"arithmetic-operators",
298+
"string-formatting",
299+
"packages"
300+
],
301+
"status": "beta"
285302
}
286303
],
287304
"practice": [
@@ -1928,6 +1945,11 @@
19281945
"name": "Comparison",
19291946
"slug": "comparison",
19301947
"uuid": "f7abb288-9526-447d-9eb8-45d756f2ae9b"
1948+
},
1949+
{
1950+
"name": "Pointers",
1951+
"slug": "pointers",
1952+
"uuid": "57477550-cecf-4334-903a-85a6c32d61d0"
19311953
}
19321954
],
19331955
"key_features": [

0 commit comments

Comments
 (0)