Memory Is Just An Array Of Bytes
2025-04-26
Memory is just an array of bytes.
Everything, but everything - opcodes, data, ASTs, strings, callstack, etc, etc - goes into that array.
"Pointer" is just a fancy word for "index".
Programming languages and "data structures" are just skins that help programmers think about what's in the array called "memory".
With early assembler, all of memory was treated just like bytes. With C, it gets a bit better - you get to overlay skins that look like bytes, ints, etc. With more recent languages, you get more user-defined thingies. It's all bytes in the end, and indices into the array of bytes (actually, it's all atomic particles, but that's too fine a grain to think with). Data structures, types, callstacks, etc. are just overlays onto this array of bytes.
See Also
Email: ptcomputingsimplicity@gmail.com
References: https://guitarvydas.github.io/2024/01/06/References.html
Blog: guitarvydas.github.io
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/65YZUh6Jpq
Leanpub: [WIP] https://leanpub.com/u/paul-tarvydas
Gumroad: tarvydas.gumroad.com
Twitter: @paul_tarvydas
Substack: paultarvydas.substack.com



In the early days of programming language development, Holt (et al, Cordy, the rest of CSRI) wrote a Working Paper on using typed "pointers" as indices into arrays of bytes, when searching for ways to improve on C and assembler programming. I don't have a reference to that WP. Paraphrasing, if there was a declaration like `xyz: array of int`, then an index/pointer to the array `xyz` was not declared as `p: pointer to int` but `p: index of xyz`. This allows the type-checker to do its thing while allowing `p` to be treated as a pointer, e.g. `p += 1` becomes `p += sizeof(int)` and `xyz[p]` becomes `*p`.
PUSM - https://programmingsimplicity.substack.com/p/pusm-programming-using-the-sicp-method?r=1egdky - has been dragging us away from the ethos of figuring out how to use CPUs efficiently. Holt, Cordy, Fraser/Davidson, etc. were on a different track that might benefit from being revisited, now that we have faster and better hardware and better techniques.
Notice, also, the use of the word "evade". That word smacks of the word "epicycle" from the days of Copernicus. This should be a red flag. The fact that a "feature" needs to be evaded means that something is deeply unsatisfactory with the "feature". Probably the feature is being applied well outside of its sweet spot. Kind of like the concepts of "assignment" and "heaps" shoved into the functional paradigm. The idea of "assign once" causes cognitive dissonance in the minds of programmers who face the reality of how CPUs work (CPU == sequencer chip that works with mutable memory chips bolted to it).
You can't have it all at once, a feature that solves one kind of problem should not be assumed to solve every kind of problem. The very idea of "general purpose" programming languages is rife with cognitive dissonances. We need special purpose programming languages. We used to have them - Prolog, Forth, pre-CL Lisp, Icon, etc. - but, we've settled on chasing only one paradigm (FP and function-based) for decades instead.
I've been thinking this too, especially with regards to the Rust idiom of evading the borrow checker by putting items in an array and referencing them via index.
I wonder if it would make any sense for a language to allow the same or similar abstractions used on memory to be applied to other arrays