A storage class specifier shapes an object's storage duration, linkage, scope-related behavior, or thread ownership. The main distinctions become clear by comparing ordinary local definitions, extern declarations, static-duration objects, and thread_local data. section 13.2 section 20.3
The storage-class rules connect declarations to object lifetime and visibility:
- Most block-scope variables without static storage duration are automatic: they live for execution of their defining block, and recursive calls create separate instances. section 13.2.2
- extern declarations designate an existing object with static storage duration; a declaration can shadow another identifier while still referring to the same object. section 13.2
- register objects prohibit address-taking and therefore cannot alias through an obtained address; the treatment recommends them for suitable local variables in performance-critical code. section 13.2.2
- thread_local creates one separate instance per thread and is appropriate when initialization can be determined at compile time; dynamic setup instead uses tss_t. section 20.3
Shadowing does not necessarily create another object
unsigned i = 1; int main(void) { unsigned i = 2; if (i) { extern unsigned i; printf("%u\n", i); /* prints 1 */ } } The block-scope extern declaration makes the outer object visible inside the condition block, while the local definition creates a distinct object and shadows the file-scope identifier elsewhere. section 13.2

