The most common reason is you don't want all code to see those symbols, for example you may be mapping system APIs or variables to common symbols but also want to reduce the chance they could be corrupted by rogue or poor quality code, an example from one of my own projects is main_thread, I need it for synchronising spawned threads but don't want some external code trying to fiddle with it in ways it should, therefore both the contents of main_thread and the symbol itself are only declared inside the code that becomes part of the shared libarry I build, external code only gets to see the pointer type, anything else is completely hidden.