//X
int num = 5;
int *ptr;
ptr = #
For the above, the int value "num" is allocated on stack, or in program data segment so it has an address. Lets pretend it was assigned by the compiler the address 0x12345678. You create an int* ptr. This also has address lets say 0x20000000. The address currently point to random data. We want to make the pointer at 0x20000000 point to the data value at 0x12345678, so that we can read the contents of 0x12345678 and get back the value 5... so we place 0x12345678 inside the storage space at 0x20000000 (we set ptr = &num).
//Z
int* myptr;
*myptr = 5;
For the second example, we only have 0x20000000 (myptr). Its a pointer and it currently pointing nowhere or anywhere. When we do *myptr = 5, we look at the address stored at 0x20000000. Its random so it may be 0xffff0000 lets use that example. It will then try and write the value 5 to this address (0xffff0000) which does not exist and causes the segfault.
So in your last example, the pointer exists, but it does not point anywhere valid, so when you try to write where it points, you either corrupt valid memory or cause a segment fault.