Memory is a term describing the the semi-permanent (permanent while it receives power) state of data as it exists in fast storage. If you try to access random areas of said memory, you will be sorely disappointed as the OS instead allocates you memory as needed and barriers tasks from accessing each other's memory.
In other words, location 0x0000ffff in your application does not map to system location 0x0000ffff, but instead a translated portion of said block. In addition, there are no guarantees as to how that memory will be ordered/allocated/segmented outside of specific requests for a contiguous block of memory via something like malloc. You can assume your array (static and dynamic) is contiguous, but that's the only assumption you can make.
C requires an OS to provide CRT and syscalls for it to function. So you're correct in that the memory model is more akin to "what the OS decides to offer you", with the singular guarantee that specific pieces of data will be contiguous.
If you were to write freestanding C code, this certainly changes. At which point the memory model becomes "what you decide to provide".
The C memory model is what imposes pointer provenance and all that jazz on C programs. The OS provides an abstraction over physical memory, and programming languages abstract over individual address spaces.
> That doesn't make sense; the OS itself is written in C.
Please point to a non-userspace memory allocation library/implementation that does not rely on lower level logic to function.
> Okay, so we're back to, C doesn't impose anything on you, though the OS may.
You're just reversing the original statement.
The statement was "you can't assume anything about memory in C" (paraphrasing). They then asked "why not?"
The explanation is that:
What you think is 'memory' in C isn't, and certainly doesn't map to what most people assume about memory; because C doesn't impose a memory model, it relies on the underlying OS/environment to do so. The only "memory model" is thus: a requested allocation (whether on the stack or heap), if provided to you, will match your request; all other assumptions are invalid.
If you want to move goalposts, argue about logical boundaries, etc, have at it. Or if that answer doesn't satisfy you, I don't know what to tell you; but simply rephrasing the original problem does even less.
I was just making it clear that the OS policy pervades. That is, the developers of CPython or another execution context need to care about it, and so it informs some of the implementation details that may leak into programs. We agree that Python presents an abstraction so that Python programmers (usually) don't have to see this.
I mean, sure, that's also a valid description. If we're to be precise, at least on x86 the OS sets up a global descriptor table[0] and then uses the LGDT instruction to tell the CPU to use it[1], and LGDT is a privileged instruction so only the OS (kernel) can do that.
This isn't really responsive to the question. The C memory model is basically defined in stdatomic.h [0]. It's around 10 pages and describes affordances you almost never, ever see.
> In addition, there are no guarantees as to how that memory will be ordered/allocated/segmented outside of specific requests for a contiguous block of memory via something like malloc. You can assume your array (static and dynamic) is contiguous...
The standard can only be referring to the abstract machine. In truth, your OS might give you 1,000,000 elements on 1 page, and 1,000,000 elements on a different page, which exist nowhere near each other in RAM (or have been swapped to disk, etc. etc.), or are being CoW'd into existence, and so on.
This is empirically true--from the days when programs like Chromium would try and malloc all the memory in a machine. The pointer that malloc returned could not have referred to a contiguous memory block. You can try it on your machine by malloc'ing more memory than you have and then reading from the blob.
> The standard can only be referring to the abstract machine. In truth, your OS might give you 1,000,000 elements on 1 page, and 1,000,000 elements on a different page, which exist nowhere near each other in RAM (or have been swapped to disk, etc. etc.), or are being CoW'd into existence, and so on.
This is where the "model" portion of the statement comes into play and why OPs point is even more cogent. The memory will be virtually contiguous, even if it's physically disparate.
> The memory will be virtually contiguous, even if it's physically disparate.
I don't really understand the value of extolling the fact that like, you can incrementally iterate through an array in C. The days of systems with segmented memory are long behind us, and even then it wasn't like you'd have an array that spanned segments--you couldn't! Honestly what language/platform exists that doesn't have this property and what would that even look like? Like you'd somehow have to know that elements 100-200 in an array are "no good" and you have to skip them?
People keep trying to put meat on the very basic bones you've got here, but you keep insisting that, yep, 2 comes after 1 and 717 comes after 716. Great! We know! And we say stuff about how trying to index off the end of an array leads to UB, or how the array may not actually be contiguous in physical memory, or it might be in various caches, or how sometimes your array goes from 1024 members to 1025 members and your FPS drops from 300 to 30 because you fell out of cache, and you're like, "sure but you still access arrays with consecutive indexes". Well, yeah! It's kind of the point of using C that you have access to or some control over these kinds of things. They're useful to the conversation. Continually bringing us back to indexing... I don't think is.
> This is where the "model" portion of the statement comes into play and why OPs point is even more cogent.
Eh, "memory model" is a specific phrase referring to how memory is defined to work in a threaded environment [0]. The original "Help us understand: what is the model of memory in C?" prompt is referring to the fact that unless you prolifically use the API in stdatomic.h (which very few things do), you just have undefined behavior all over the place if you ever dare to use anything related to threads. Also, it was only defined in C11--not a lot of things have updated, even now.
---
Overall I want to emphasize that this whole thread is doing a real good job of proving Ned Batchelder's point: even people who think they know or understand C don't (to be clear, I do not think I understand C), and things are generally alright. There's--clearly, reading through everything--a culture of "You need to be an expert in some low-level, 'real' language/platform before you can write meaningful software", but you don't, and my evidence is Facebook, maybe the most influential software ever written.
In other words, location 0x0000ffff in your application does not map to system location 0x0000ffff, but instead a translated portion of said block. In addition, there are no guarantees as to how that memory will be ordered/allocated/segmented outside of specific requests for a contiguous block of memory via something like malloc. You can assume your array (static and dynamic) is contiguous, but that's the only assumption you can make.