A pointer has to point to some location in memory, so never write
int* x;
*x=...;
But write
int* x=expression that results in an valid address;
First consider a simple computer without MMU and a fancy multitasking OS. Then you can use any address, but you have to know you do not replace your code, and any important data. You will benefit from having a reserved space for code, the call stack and perhaps other data. Some machines connects addresses to peripherals, so writing there would make some physical stuff happen. This is called memory mapped I/O. To know what you are doing, you create your address layout, which on a 16 bit machine may look like
+------------------+ 0x0000
| Memory mapped IO |
+------------------+ 0x0100
| Program |
| |
+------------------+ 0x4000
| Call stack |
| (local variables)|
+------------------+ 0x8000
| Other data |
| globals |
| heap |
+------------------+ 0xffff
This fictive machine has 255 I/O ports (0x0000 is reserved), and will start program on address 0x0100. The remaining part of the address layout is up to you. Here 16128 bytes is reserved for the program , 16384 bytes for the stack and 32767 bytes left for other data. If you know that your program never becomes larger than 8 kB, then feel free to increase your call stack or heap. A program that sets the rotation speed of a motor may look like
int main()
{
const uint8_t* speed_regulator=(const uint8_t*)0x0001; //Port for speed controller is 0x0000
uint8_t* motor_voltage=(uint8_t*)0x0002; //Port for motor controller
while(true)
{
*motor_voltage=*speed_regulator;
}
}
Some care must be taken here, so the compiler does not optimise out the loop, which is our data pump.
Now you want a multitasking system with virtual memory and all modern stuff. But then you cannot use arbitrary addresses any more. To make it possible to give you a valid address, and to inform the OS about how much memory you need, the function malloc
exists.