| feature |
Registers |
Memory |
| count |
very few |
vast |
| speed |
fast |
slow |
| Named |
yes |
no |
| Addressable |
no |
yes |
There are typically 32 or fewer registers for programmers to use. On a 32-bit machine we can address 2^32 different bytes of memory.
Registers are fast, while memory is slow, potentially taking dozens of cycles depending on cache features & conditions.
On a load-store machine, registers can be used directly in most instructions (by naming them in the machine code instruction), whereas memory access requires a separate load or store instruction. Computational instructions on such a machine typically allows naming up to 3 registers (e.g. target, source1, source2). Memory operands have to be brought into registers for computation (and sometimes moved back to memory).
Register can be named in instructions, but they do not have addresses and cannot be indexed. On MIPS no register can be found as alias at some address in memory. It is hard to put even a smallish array (e.g. array of 10) in registers because they have no addresses and cannot be indexed. Memory has numerical addresses, so we can rely on storing arrays and objects in a predictable pattern of addresses. (Memory generally doesn't have names, just addresses; however, there are usually special memory locations for working with I/O various devices, and, as you note memory is partitioned into sections that have start (and ending) addresses.)
To be clear, memory-based aliases have been designed into some processors of the past. The HP/1000 (circa 70s'-80's), for example, had 2 registers (A & B), and they had aliases at memory locations 0 and 1, respectively. However, this aliasing of CPU registers to memory is generally no longer done on modern processors.
For example what the address of 8($t0) would be
8($t0) refers to the memory address of (the contents of register $t0) + 8. With proper usage, the program fragment would $t0 would be using $t0 as a pointer, which is some variable that holds a memory address.