A local object and its lifetime
Consider `void f(void) { int value = 3; { int other = value; } }`. `value` has automatic storage duration: its lifetime normally corresponds to the execution of `f`’s block. `other` has the same duration, but its lifetime corresponds to the inner block. Leaving a block ends the lifetime of its ordinary automatic objects, even if a pointer to one remains. Referring to an object outside its lifetime fails. section 13.2.2 section 13.2
Automatic storage duration describes when storage is available at run time; it is not the same as an identifier’s visibility. Block-scope variables and compound literals generally have automatic duration unless declared `static`; eligible declarations include those without a storage-class specifier and those using `auto`, `register`, or `constexpr`. Some function-call results are temporary automatic objects too. The simple block-lifetime rule has exceptions for variable-length arrays (VLAs) and temporary objects. section 13.2.2 section 13.2
TAKEAWAY — follow the object’s lifetime, not just the spelling of its name.
- Each recursive call creates a separate local instance of an automatic object; multiple instances can therefore exist at once. section 13.2.2
- Initialize automatic objects explicitly. Unlike objects with static or thread storage duration, they are not initialized by default. section 13.4
- A `register` declaration prohibits taking the object’s address with `&`. This can rule out aliasing for suitable non-array objects; arrays need separate consideration. section 13.2.2
A local name can shadow another without ending the older object’s lifetime. If a pointer to the older object was obtained before the shadowing declaration, it can still designate that object while it remains alive. Visibility answers which declaration a name denotes; lifetime answers whether the object may be accessed. section 13.2
Listing 13.1 An example of shadowing with local variables
#include "c23-fallback.h"
void squareIt(double* p) [[__unsequenced__]] {
*p *= *p;
}
int main(void) { 1
double x = 35.0; 2
double* xp = &x;
{ 3
squareIt(&x); /* Refers to double x */
4
[[maybe_unused]] int x = 0; /* Shadow double x */
5
squareIt(xp); /* Valid use of double x */
6
}
7
squareIt(&x); /* Refers to double x */
}
The inner `int x` shadows the outer `double x`, but the previously saved pointer `xp` still permits access to the outer object during its lifetime. section 13.2
Automatic duration does not require one fixed machine layout. A gcc x86_64 assembler example places local values at offsets from a base pointer; an optimized version uses registers and different instructions. Those listings illustrate possible implementations, not a portable C rule about where every local object resides. section 13.5

