teppic
teppic

Reputation: 8195

Variable definitions in a loop

I've been trying to get into the habit of defining trivial variables at the point they're needed. I've been cautious about writing code like this:

while (n < 10000) {
   int x = foo();
   [...]
}

I know that the standard is absolutely clear that x exists only inside the loop, but does this technically mean that the integer will be allocated and deallocated on the stack with every iteration? I realise that an optimising compiler isn't likely to do this, but it that guaranteed?

For example, is it ever better to write:

int x;
while (n < 10000) {
   x = foo();
   [...]
}

I don't mean with this code specifically, but in any kind of loop like this.

I did a quick test with gcc 4.7.2 for a simple loop differing in this way and the same assembly was produced, but my question is really are these two, according to the standard, identical?

Upvotes: 3

Views: 146

Answers (4)

Art
Art

Reputation: 20392

There is nothing in the C standard that says how the compiler should generate code in either case. It could adjust the stack pointer on every iteration of the loop if it fancies.

That being said, unless you start doing something crazy with VLAs like this:

void bar(char *, char *);
void
foo(int x)
{
        int i;
        for (i = 0; i < x; i++) {
                char a[i], b[x - i];
                bar(a, b);
        }
}

the compiler will most likely just allocate one big stack frame at the beginning of the function. It's harder to generate code for creating and destroying variables in blocks instead of just allocating all you need at the beginning of the function.

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 753525

Yes, the variable x inside the loop is technically defined on each iteration, and initialized via the call to foo() on each iteration. If foo() produces a different answer each time, this is fine; if it produces the same answer each time, it is an optimization opportunity – move the initialization out of the loop. For a simple variable like this, the compiler typically just reserves sizeof(int) bytes on the stack — if it can't keep x in a register — that it uses for x when x is in scope, and may reuse that space for other variables elsewhere in the same function. If the variable was a VLA — variable length array — then the allocation is more complex.

The two fragments in isolation are equivalent, but the difference is the scope of x. In the example with x declared outside the loop, the value persists after the loop exits. With x declared inside the loop, it is inaccessible once the loop exits. If you wrote:

{
    int x;
    while (n < 10000)
    {
        x = foo();
        ...other stuff...
    }
}

then the two fragments are near enough equivalent. At the assembler level, you'll be hard pressed to spot the difference in either case.

Upvotes: 1

Robbie Dee
Robbie Dee

Reputation: 1977

My personal point of view is that once you start worrying about such micro-optimisations, you're doomed to failure. The gain is:

a) Likely to be very small

b) Non-portable

I'd stick with code that makes your intention clear (i.e. declare x inside the loop) and let the compiler care about efficiency.

Upvotes: 1

unwind
unwind

Reputation: 399753

Note that "allocating" automatic variables like this is pretty much free; on most machines it's either a single-instruction stack pointer adjustment, or the compiler uses registers in which case nothing needs to be done.

Also, since the variable remains in scope until the loop exits, there's absolutely no reason to "delete" (=readjust the stack pointer) it until the loop exits, I certainly wouldn't expect there to be any overhead per-iteration for code like this.

Also, of course the compiler is free to "move" the allocation out of the loop altogether if it feels like it, making the code equivalent to your second example with the int x; before the while. The important thing is that the first version is easier to read and more tighly localized, i.e. better for humans.

Upvotes: 6

Related Questions