int f; f = 2;
To me this says, there is an int pointer f. Let f point to 2. Is this not possible only because 2 does not occupy memory? I don't see why this couldn't be valid.
int *f; *f = 2;
The statement, *f = 2;
says "dereference f to get a location; set the contents of that location to 2". "Set f to the location of 2" would be spelled f = &2;
This is partly a matter of the difference between assignment and definition(/equality).The first of these is invalid C (undefined behavior) because it uses a value (the contents of f) before initialization.
The second is invalid C (compile error) because 2 is not an l-value (that is, it does not have a location).
Edited to add: I will note that I don't think there is any reason
*f = 2;
couldn't mean to give f the addess of 2, as by analogy to structural pattern matching, but that the syntax is already taken for something else.In those cases some compiler override could provide the solution, while still allowing the compiler to flag all the other cases as errors.
If you use the volatile key word, then you are guaranteed that all reads and stores will actually happen and not be optimized out. But volatile still allows non-atomicity and reordering so you probably want something which prohibits that too.
Initialization of a variable has no relation to whether it has a memory location. You can legitimately take the address of an uninitialized variable (that is often one step in initializing them, such as when you pass the address to memset), and even an initialized variable may not have a memory location (if it lives only in register, or is compiled away entirely).