The variable memory on the stack is allocated at the moment of entering the block of curly brackets. Accordingly, when braces are exceeded, all local variables for this block are killed. Although you cannot contact them outside the block. The only nuance is the type design
for (int i=0;...) {...}
which, depending on the compiler and / or its options, can unfold into
int i; for (i=0;...) {...} // здесь мы можем с i вытворять дальнейшие упражнения :-)
or
... for (int i=0; ...) {...} // здесь i уже неопределена, т.к. локальна для блока {...} под for
I also think that smearing the creation of variables in blocks is not very rational. The downside is: you can easily create two identical different variables and get confused between them.
... int i; ... { int i; ... i = 10; // меняем значение "внутренней" i ::i = 10; // меняем значение i из внешнего блока, но при этом если уровней вложенности несколько, то доступ к локальным i из промежуточных блоков, если они там, конечно, есть, теряется. }
At the same time, the visibility of the description of variables, which is in pascal (the block of definitions of var), disappears. It is reasonable to locally create objects that should be automatically destroyed. This is useful when using "smart pointers" that automatically kill the target object. And there is no headache - we have freed the memory or not. It is convenient that the object lives strictly where it is needed. Where it is not needed - simply do not turn to it.