Skip to content

Commit 6d2cb6c

Browse files
committed
trpl: Fix off-by-one highest memory address
1 parent 2be0d0a commit 6d2cb6c

File tree

1 file changed

+86
-86
lines changed

1 file changed

+86
-86
lines changed

src/doc/trpl/the-stack-and-the-heap.md

+86-86
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,18 @@ on the heap. The actual value of the box is a structure which has a pointer to
217217
it allocates some memory for the heap, and puts `5` there. The memory now looks
218218
like this:
219219

220-
| Address | Name | Value |
221-
|-----------------|------|------------------|
222-
| 2<sup>30</sup> | | 5 |
223-
| ... | ... | ... |
224-
| 1 | y | 42 |
225-
| 0 | x | → 2<sup>30</sup> |
226-
227-
We have 2<sup>30</sup> in our hypothetical computer with 1GB of RAM. And since
220+
| Address | Name | Value |
221+
|----------------------|------|------------------------|
222+
| (2<sup>30</sup>) - 1 | | 5 |
223+
| ... | ... | ... |
224+
| 1 | y | 42 |
225+
| 0 | x |(2<sup>30</sup>) - 1 |
226+
227+
We have (2<sup>30</sup>) - 1 in our hypothetical computer with 1GB of RAM. And since
228228
our stack grows from zero, the easiest place to allocate memory is from the
229229
other end. So our first value is at the highest place in memory. And the value
230230
of the struct at `x` has a [raw pointer][rawpointer] to the place we’ve
231-
allocated on the heap, so the value of `x` is 2<sup>30</sup>, the memory
231+
allocated on the heap, so the value of `x` is (2<sup>30</sup>) - 1, the memory
232232
location we’ve asked for.
233233

234234
[rawpointer]: raw-pointers.html
@@ -244,18 +244,18 @@ layout of a program which has been running for a while now:
244244

245245
| Address | Name | Value |
246246
|----------------------|------|------------------------|
247-
| 2<sup>30</sup> | | 5 |
248-
| (2<sup>30</sup>) - 1 | | |
247+
| (2<sup>30</sup>) - 1 | | 5 |
249248
| (2<sup>30</sup>) - 2 | | |
250-
| (2<sup>30</sup>) - 3 | | 42 |
249+
| (2<sup>30</sup>) - 3 | | |
250+
| (2<sup>30</sup>) - 4 | | 42 |
251251
| ... | ... | ... |
252-
| 3 | y | → (2<sup>30</sup>) - 3 |
252+
| 3 | y | → (2<sup>30</sup>) - 4 |
253253
| 2 | y | 42 |
254254
| 1 | y | 42 |
255-
| 0 | x | → 2<sup>30</sup> |
255+
| 0 | x |(2<sup>30</sup>) - 1 |
256256

257257
In this case, we’ve allocated four things on the heap, but deallocated two of
258-
them. There’s a gap between 2<sup>30</sup> and (2<sup>30</sup>) - 3 which isn’t
258+
them. There’s a gap between (2<sup>30</sup>) - 1 and (2<sup>30</sup>) - 4 which isn’t
259259
currently being used. The specific details of how and why this happens depends
260260
on what kind of strategy you use to manage the heap. Different programs can use
261261
different ‘memory allocators’, which are libraries that manage this for you.
@@ -366,105 +366,105 @@ fn main() {
366366

367367
First, we call `main()`:
368368

369-
| Address | Name | Value |
370-
|-----------------|------|------------------|
371-
| 2<sup>30</sup> | | 20 |
372-
| ... | ... | ... |
373-
| 2 | j | → 0 |
374-
| 1 | i | → 2<sup>30</sup> |
375-
| 0 | h | 3 |
369+
| Address | Name | Value |
370+
|----------------------|------|------------------------|
371+
| (2<sup>30</sup>) - 1 | | 20 |
372+
| ... | ... | ... |
373+
| 2 | j | → 0 |
374+
| 1 | i |(2<sup>30</sup>) - 1 |
375+
| 0 | h | 3 |
376376

377377
We allocate memory for `j`, `i`, and `h`. `i` is on the heap, and so has a
378378
value pointing there.
379379

380380
Next, at the end of `main()`, `foo()` gets called:
381381

382-
| Address | Name | Value |
383-
|-----------------|------|-----------------|
384-
| 2<sup>30</sup> | | 20 |
385-
| ... | ... | ... |
386-
| 5 | z | → 4 |
387-
| 4 | y | 10 |
388-
| 3 | x | → 0 |
389-
| 2 | j | → 0 |
390-
| 1 | i | → 2<sup>30</sup>|
391-
| 0 | h | 3 |
382+
| Address | Name | Value |
383+
|----------------------|------|------------------------|
384+
| (2<sup>30</sup>) - 1 | | 20 |
385+
| ... | ... | ... |
386+
| 5 | z | → 4 |
387+
| 4 | y | 10 |
388+
| 3 | x | → 0 |
389+
| 2 | j | → 0 |
390+
| 1 | i |(2<sup>30</sup>) - 1 |
391+
| 0 | h | 3 |
392392

393393
Space gets allocated for `x`, `y`, and `z`. The argument `x` has the same value
394394
as `j`, since that’s what we passed it in. It’s a pointer to the `0` address,
395395
since `j` points at `h`.
396396

397397
Next, `foo()` calls `baz()`, passing `z`:
398398

399-
| Address | Name | Value |
400-
|-----------------|------|------------------|
401-
| 2<sup>30</sup> | | 20 |
402-
| ... | ... | ... |
403-
| 7 | g | 100 |
404-
| 6 | f | → 4 |
405-
| 5 | z | → 4 |
406-
| 4 | y | 10 |
407-
| 3 | x | → 0 |
408-
| 2 | j | → 0 |
409-
| 1 | i | → 2<sup>30</sup> |
410-
| 0 | h | 3 |
399+
| Address | Name | Value |
400+
|----------------------|------|------------------------|
401+
| (2<sup>30</sup>) - 1 | | 20 |
402+
| ... | ... | ... |
403+
| 7 | g | 100 |
404+
| 6 | f | → 4 |
405+
| 5 | z | → 4 |
406+
| 4 | y | 10 |
407+
| 3 | x | → 0 |
408+
| 2 | j | → 0 |
409+
| 1 | i |(2<sup>30</sup>) - 1 |
410+
| 0 | h | 3 |
411411

412412
We’ve allocated memory for `f` and `g`. `baz()` is very short, so when it’s
413413
over, we get rid of its stack frame:
414414

415-
| Address | Name | Value |
416-
|-----------------|------|------------------|
417-
| 2<sup>30</sup> | | 20 |
418-
| ... | ... | ... |
419-
| 5 | z | → 4 |
420-
| 4 | y | 10 |
421-
| 3 | x | → 0 |
422-
| 2 | j | → 0 |
423-
| 1 | i | → 2<sup>30</sup> |
424-
| 0 | h | 3 |
415+
| Address | Name | Value |
416+
|----------------------|------|------------------------|
417+
| (2<sup>30</sup>) - 1 | | 20 |
418+
| ... | ... | ... |
419+
| 5 | z | → 4 |
420+
| 4 | y | 10 |
421+
| 3 | x | → 0 |
422+
| 2 | j | → 0 |
423+
| 1 | i |(2<sup>30</sup>) - 1 |
424+
| 0 | h | 3 |
425425

426426
Next, `foo()` calls `bar()` with `x` and `z`:
427427

428428
| Address | Name | Value |
429429
|----------------------|------|------------------------|
430-
| 2<sup>30</sup> | | 20 |
431-
| (2<sup>30</sup>) - 1 | | 5 |
430+
| (2<sup>30</sup>) - 1 | | 20 |
431+
| (2<sup>30</sup>) - 2 | | 5 |
432432
| ... | ... | ... |
433433
| 10 | e | → 9 |
434-
| 9 | d | → (2<sup>30</sup>) - 1 |
434+
| 9 | d | → (2<sup>30</sup>) - 2 |
435435
| 8 | c | 5 |
436436
| 7 | b | → 4 |
437437
| 6 | a | → 0 |
438438
| 5 | z | → 4 |
439439
| 4 | y | 10 |
440440
| 3 | x | → 0 |
441441
| 2 | j | → 0 |
442-
| 1 | i | → 2<sup>30</sup> |
442+
| 1 | i |(2<sup>30</sup>) - 1 |
443443
| 0 | h | 3 |
444444

445445
We end up allocating another value on the heap, and so we have to subtract one
446-
from 2<sup>30</sup>. It’s easier to just write that than `1,073,741,823`. In any
446+
from (2<sup>30</sup>) - 1. It’s easier to just write that than `1,073,741,822`. In any
447447
case, we set up the variables as usual.
448448

449449
At the end of `bar()`, it calls `baz()`:
450450

451451
| Address | Name | Value |
452452
|----------------------|------|------------------------|
453-
| 2<sup>30</sup> | | 20 |
454-
| (2<sup>30</sup>) - 1 | | 5 |
453+
| (2<sup>30</sup>) - 1 | | 20 |
454+
| (2<sup>30</sup>) - 2 | | 5 |
455455
| ... | ... | ... |
456456
| 12 | g | 100 |
457457
| 11 | f | → 9 |
458458
| 10 | e | → 9 |
459-
| 9 | d | → (2<sup>30</sup>) - 1 |
459+
| 9 | d | → (2<sup>30</sup>) - 2 |
460460
| 8 | c | 5 |
461461
| 7 | b | → 4 |
462462
| 6 | a | → 0 |
463463
| 5 | z | → 4 |
464464
| 4 | y | 10 |
465465
| 3 | x | → 0 |
466466
| 2 | j | → 0 |
467-
| 1 | i | → 2<sup>30</sup> |
467+
| 1 | i |(2<sup>30</sup>) - 1 |
468468
| 0 | h | 3 |
469469

470470
With this, we’re at our deepest point! Whew! Congrats for following along this
@@ -474,44 +474,44 @@ After `baz()` is over, we get rid of `f` and `g`:
474474

475475
| Address | Name | Value |
476476
|----------------------|------|------------------------|
477-
| 2<sup>30</sup> | | 20 |
478-
| (2<sup>30</sup>) - 1 | | 5 |
477+
| (2<sup>30</sup>) - 1 | | 20 |
478+
| (2<sup>30</sup>) - 2 | | 5 |
479479
| ... | ... | ... |
480480
| 10 | e | → 9 |
481-
| 9 | d | → (2<sup>30</sup>) - 1 |
481+
| 9 | d | → (2<sup>30</sup>) - 2 |
482482
| 8 | c | 5 |
483483
| 7 | b | → 4 |
484484
| 6 | a | → 0 |
485485
| 5 | z | → 4 |
486486
| 4 | y | 10 |
487487
| 3 | x | → 0 |
488488
| 2 | j | → 0 |
489-
| 1 | i | → 2<sup>30</sup> |
489+
| 1 | i |(2<sup>30</sup>) - 1 |
490490
| 0 | h | 3 |
491491

492492
Next, we return from `bar()`. `d` in this case is a `Box<T>`, so it also frees
493-
what it points to: (2<sup>30</sup>) - 1.
494-
495-
| Address | Name | Value |
496-
|-----------------|------|------------------|
497-
| 2<sup>30</sup> | | 20 |
498-
| ... | ... | ... |
499-
| 5 | z | → 4 |
500-
| 4 | y | 10 |
501-
| 3 | x | → 0 |
502-
| 2 | j | → 0 |
503-
| 1 | i | → 2<sup>30</sup> |
504-
| 0 | h | 3 |
493+
what it points to: (2<sup>30</sup>) - 2.
494+
495+
| Address | Name | Value |
496+
|----------------------|------|------------------------|
497+
| (2<sup>30</sup>) - 1 | | 20 |
498+
| ... | ... | ... |
499+
| 5 | z | → 4 |
500+
| 4 | y | 10 |
501+
| 3 | x | → 0 |
502+
| 2 | j | → 0 |
503+
| 1 | i |(2<sup>30</sup>) - 1 |
504+
| 0 | h | 3 |
505505

506506
And after that, `foo()` returns:
507507

508-
| Address | Name | Value |
509-
|-----------------|------|------------------|
510-
| 2<sup>30</sup> | | 20 |
511-
| ... | ... | ... |
512-
| 2 | j | → 0 |
513-
| 1 | i | → 2<sup>30</sup> |
514-
| 0 | h | 3 |
508+
| Address | Name | Value |
509+
|----------------------|------|------------------------|
510+
| (2<sup>30</sup>) - 1 | | 20 |
511+
| ... | ... | ... |
512+
| 2 | j | → 0 |
513+
| 1 | i |(2<sup>30</sup>) - 1 |
514+
| 0 | h | 3 |
515515

516516
And then, finally, `main()`, which cleans the rest up. When `i` is `Drop`ped,
517517
it will clean up the last of the heap too.

0 commit comments

Comments
 (0)