The first C compilers required that automatic object declarations precede any other executable code within a function (see page 15 of https://www.bell-labs.com/usr/dmr/www/cman.pdf). Note that only objects of static duration were allowed to have initializers, and compound statements were not allowed to introduce new objects. These limitations greatly simplified single-pass compilation, and fit very well with Dennis Ritchie's objective that C be a simple language to compile.
By the time the first C Standard was published in 1989, most compilers had extended the language to allow any compound statement to start with automatic object declarations, and to allow automatic object declarations to include initializers. These additions would sometimes make it more difficult for a single pass compiler to generate efficient code, but it generally wasn't too hard to produce correct code. For example, given something like:
int test()
{
int i,j;
i=foo();
j=bar();
a compiler would have been easily able to use one instruction to reserve stack space for both i
and j
, but if the code had been written as:
int test()
{
int i=foo(),j=bar();
a single-pass compiler may have had to generate an instruction that allocates stack space for i
, then calls foo
, then an instruction that allocates stack space for j
, and then calls bar
. Not as efficient as what would have been produced given the old syntax, but adding the ability to allocate more space wasn't a particular problem.
Although C89 allowed some executable code to precede automatic object declarations, it required that any object declarations within a compound statement block precede any other statements within that block. The C99 standard relaxed this rule, and also allowed the first cause of a for
loops to be an automatic object declaration rather than an expression. Although there are some machines for which C99 compilers are not available, the ability to declare a loop's index as part of a for
loop is sufficiently useful that it's often desirable to write code that way.
The biggest downside to using that form is that if one has two loops within a block, and it becomes necessary to change one of them or the surrounding code so that the index object gets assigned the loop or examined after it, many compilers would squawk at a pattern like:
void test(void)
{
for (int i=0; i<50; i++)
doSomethingWith(i);
int i;
for (i=; i<50 && shouldntEarlyExit(i); i++)
doSomethingWith(i);
doSomethingWithWhatWoudlHaveBeenNextValueOf(i);
}
Although the Standard would allow the name i
to be used both as the name of an automatic object whose scope is limited to the first loop, and also as an automatic variable whose scope is the enclosing block, many compilers will issue warning about such name reuse because it is often the result of programming mistakes. Thus, if it becomes necessary to rewrite any of the loops so that the index value is loaded before the loop or examined afterward, it may be necessary to remove the declaration from all loops using that name. Declaring the index at the start of the function rather than declaring it at the start of each loop will avoid the need to remove declarations from looping statements later.