3

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?

3
  • As you say yourself about the first loop: "I know that the standard is absolutely clear that x exists only inside the loop", and as in the second example, the variable x is usable after the loop those two can not possible be identical, or the standard would be ambiguous. Commented Mar 14, 2013 at 13:13
  • @JoachimPileborg: well, yes - but as far as the loop is concerned. Commented Mar 14, 2013 at 13:15
  • If you just consider the loop and nothing else, then yes they are identical. Commented Mar 14, 2013 at 13:24

4 Answers 4

6

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Surely though because the variable is being defined (not just accessed) in the loop, it'd have to go out of scope (at least technically)? As otherwise the next iteration would try to define another instance of an existing variable?
Technically, it goes out of existence. In practice, that means (a) you can't reference x outside the loop, and (b) the compiler stops using the space it allocated for x to store x (and it may start using that same space to store a different variable in the following code). There's not likely to be any other changes than whether the compiler references the memory location or not.
1

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.

1 Comment

You're quite right about the difference of the scope of x of course -- I made the question less clear because of that, but your example fixes the comparison.
1

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.