Previously, we've used gcc -S to stop assembling and gcc -c to stop linking, and explored C variables representation in assembly(gcc -S) and object(gcc -c).
The symbols in the intermediate object file (vars-section.o) are waiting to be relocated and resolved to determine their actual virtual address. It's the linker's turn.
After linking, all object files (*.o) are linked into one ELF. By default, it links dynamically and the type of outcome is DYN (position-independent executable file).
The "happens before" relation is the only possible way to reason about timing between different threads. It is only established through synchronization that uses either atomic objects or very specific C library functions.
An atomic object can be used to synchronize two threads, if one thread writes a value and another thread reads the value that was written. Operations on atomics are guaranteed to be locally consistent.
Sequential consistency is the default consistency model for atomics, but not for other C library functions. It additionally assumes that all corresponding synchronization events are totally ordered.
volatile in C actually came into existence for the purpose of not caching the values of the variable automatically. It will tell the compiler not to cache the value of this variable. So it will generate code to take the value of the given volatile variable from the main memory every time it encounters it. This mechanism is used because at any time the value can be modified by the OS or any interrupt. So using volatile will help us accessing the value afresh every time.
There is yet another construct for which the address-of operator & can be used: functions.
A function pointer, also called a subroutine pointer or procedure pointer, is a pointer referencing executable code, rather than data. Dereferencing the function pointer yields the referenced function, which can be invoked and passed arguments just as in a normal function call. Such an invocation is also known as an "indirect" call, because the function is being invoked indirectly through a variable instead of directly through a fixed identifier or address.
Function pointers allow different code to be executed at runtime. They can also be passed to a function to enable callbacks.
The advantage of using function pointers is that multiple modules implementing the same function can be identified together, making maintenance easier and the system structure clearer. Or to summarise: it is easy to design layers, promotes system abstraction, reduces coupling and separates the interface from the implementation.
Previously, we've discussed const qualifier and Named Constants in C. Today let's go back to the root and have a look at null pointers and pointer validity. After all, everything starts at zero and disappears without a trace.