One line: in the first code, you are playing an uninitialized pointer that demonstrates undefined behavior, and in the second code, you are looking for an initialized pointer that will give access to the value at the address.
A little explanation:
First you need to understand that a pointer is nothing more than an integer, but with *var we tell the compiler that we will use the contents of the var variable (the integer in it) as the address for select the value in this address. If **var exists, then we tell the compiler that we will first use the stored value of the var variable to retrieve the value at the address and again use this extracted value as the address and get the value stored in it.
So in your first ad this is:
+----------+ +----------+ | garbage | | garbage | +----------+ +----------+ | a | | b | +----------+ +----------+ | addr1 | | addr2 | +----------+ +----------+
Then you try to use the value stored in a as the address. a contains garbage, it can be any value, but you do not have access to any address. Therefore, the next moment when you execute *a , it will use the stored value in a as the address. Since a stored value can be anything, anything can happen.
If you have permission to access the location, the code will continue to run without segmentation failures. If the address is an address from the heap storage structure or another area of ββmemory that your code allocates from the heap or stack, then when you execute *a = 10 , it will simply destroy the existing value with 10 at this location, This can lead to undefined behavior , because now you have changed something, not knowing the context that has the actual power of memory. If you do not have memory rights, you simply get a segmentation error. This is called dereferencing an uninitialized pointer.
The next statement that you execute a = &b , which simply assigns the address b to a . This does not help, as the previous line dereferences the uninitialized pointer.
In the following code, you have something similar after the third statement:
+----------+ +----------+ | addr2 |---+ | garbage | +----------+ | +----------+ | a | +--> | b | +----------+ +----------+ | addr1 | | addr2 | +----------+ +----------+
The third statement assigns address b to a . Prior to this, a not dereferenced, so the garbage value stored in a before initialization is never used as an address. Now that you assign the actual address of your knowledge to a , dereferencing a will now give you access to the value pointed to by a .
Expanding the answer, you need to make sure that even if you assign a valid address to the pointer, you must make sure that the pointer that the pointer points to does not expire during the dereferencing of the pointer. For example, a returning local variable.
int foo (void) { int a = 50; return &a;
The same is the case when accessing a free memory cell from the heap.
int main (void) { char *a = malloc (1); *a = 'A';