An address space is a program’s way of locating data in memory. Picture its addresses as an orderly line of numbered bytes: an address identifies one byte, and a pointer uses an address to locate a value. The line is a useful model, but it is not a map of physical RAM. The operating system and CPU provide the program’s view of memory. section 6.1
See where values live
In Rust, `let b = &B` creates a reference to an array, and `println!("{:p}", b)` prints the address it points to. The location need not be known before the program runs. Try printing addresses for a global, a local variable, and boxed values: their addresses may be far apart even when the program uses little memory. section 6.2 section 6.4.2
Figure 6.3 An illustrative address space of the program provided in listing 6.1. It provides an illustration of the relationship between addresses (typically written in hexadecimal) and integers (typically written in decimal). White cells represent unused memory.
The small, imaginary address space makes the byte-by-byte layout visible. Read the hexadecimal addresses as positions in one shared space, including its unused cells. section 6.2
A process has its own virtual address space. The operating system manages mappings from virtual memory to real memory with page tables, while the CPU’s memory-management unit translates addresses. Memory is handled in pages rather than by keeping a separate mapping for every variable. This lets the operating system move inactive pages to disk or share physical pages while a program still sees its own addresses. section 6.4.1 section 6.4.3 section 10.5
A large range of possible addresses does not mean every address can be read. Trying to scan arbitrary addresses byte by byte can access an illegal location and end the program. To explore safely, first inspect known values’ addresses; for a fuller map of memory regions, ask the operating system rather than dereferencing every possible pointer. section 6.4.2 section 6.4.4
Listing 6.14 Printing the address of variables within a program
static GLOBAL: i32 = 1000; #1
fn noop() -> *const i32 {
let noop_local = 12345; #2
&noop_local as *const i32 #3
}
fn main() {
let local_str = "a"; #4
let local_int = 123; #4
let boxed_str = Box::new('b'); #4
let boxed_int = Box::new(789); #4
let fn_int = noop(); #4
println!("GLOBAL: {:p}", &GLOBAL as *const i32);
println!("local_str: {:p}", local_str as *const str);
println!("local_int: {:p}", &local_int as *const i32);
println!("boxed_int: {:p}", Box::into_raw(boxed_int));
println!("boxed_str: {:p}", Box::into_raw(boxed_str));
println!("fn_int: {:p}", fn_int);}
Run the Rust example to print addresses of global, local, and boxed values. Compare the resulting clusters instead of expecting every value to sit next to every other one. section 6.4.2
An address space can also be used as an isolation boundary. Separate processes have their own virtual address spaces. WebAssembly adds another boundary inside a process: a module accesses its linear memory without seeing the process’s other memory addresses. section 10.5 section 10.5.4


