Local variables within looping constructs

M

masood.iqbal

My simplistic mind tells me that having local variables within looping
constructs is a bad idea. The reason is that these variables are
created during the beginning of an iteration and deleted at the end of
the iteration. Kindly note that I am not talking about dynamically
allocating memory within a loop, which is a perfectly valid operation
(albeit with a chance of resulting in a memory leak, unless we are
careful).

The most common justification that I have heard for local variables
within looping constructs is that it helps localize the scope of the
variables. But isn't there another way to accomplish the same thing
----- by declaring a block outside the looping construct and declaring
local variables within that? Here's what I mean:

{
char buf1[100];
for(int i = ...;...;)
{
....
}
}

or

{
char buf2[100];
do
{
....
} while(...)
}


I have never seen anyone propose this approach. Are there any
drawbacks that I am not seeing?

Masood
 
E

Eric Sosman

My simplistic mind tells me that having local variables within looping
constructs is a bad idea. The reason is that these variables are
created during the beginning of an iteration and deleted at the end of
the iteration. [...]

My simplistic mind tells me that I have no idea what the
cost of creating and deleting a local variable might be. It
might be huge, in which case your aversion would make sense.
Or it might be zero, in which case you're just being silly.
It might even be negative (yes!), in which case you're
completely in the wrong camp.

It is temptingly -- too temptingly -- easy to outguess a
compiler. It is impossible to outguess all compilers. And,
of course, "It is a capital offense to theorize before one
has data." What data have you measured?
 
K

Keith Thompson

I'm not sure why this is cross-posted to comp.sources.d (I'm reading
it in comp.lang.c), but I'll leave the newsgroups header alone for
now.

My simplistic mind tells me that having local variables within looping
constructs is a bad idea. The reason is that these variables are
created during the beginning of an iteration and deleted at the end of
the iteration. Kindly note that I am not talking about dynamically
allocating memory within a loop, which is a perfectly valid operation
(albeit with a chance of resulting in a memory leak, unless we are
careful).

I wouldn't worry about it. An implementation could easily allocate
all local variables, including those in inner scopes, on entry to a
function; variables in disjoint scopes could overlap. I suspect (but
I've never checked) that most compilers actually do this.

For example (assuminging 4-byte ints, offsets measured from
something-or-other) a sensible implementation might do something like
this:

void func(void)
{
/* Allocate 12 bytes of stack space on entry */
int a; /* offset 0 */
for (blah;blah;blah) {
int b; /* offset 4;
}
while (blah) {
int c; /* offset 4 */
int d; /* offset 8 */
}
/* Deallocate 12 bytes of stack space on exit */
}

Chances are that allocating 12 bytes of stack space takes no more time
than allocating 4 bytes of stack space.

The alternative of allocating locals on entry to their scope, and
deallocating them on exit, could be expensive; it would save some
space temporarily, but the maximum usage within the function would be
the same. The only case where it might make sense is something like
this:

void strange(void)
{
int x;

do_some_stuff();
if (rare_condition) {
int huge_array[MANY];
do_some_more_stuff();
}
}

but even then the benefit is questionable.

Note that this is all about possible implementation strategies (which
I suppose makes it marginally off-topic). As far as the language is
concerned, variables are created when their scope is entered and cease
to exist when their scope is exited. But since the standard doesn't
say how the creation or destruction of a variable is to be performed,
an implementation is free to optimize it by creating it early and/or
destroying it late.

If you're really worried about it, get your compiler to generate an
assembly listing and take a look at the generated code.
 
C

Chris Williams

Chances are that allocating 12 bytes of stack space takes no more
time
than allocating 4 bytes of stack space.

That would be my guess. So far as I am aware all memory that you use
will either be:

Coming off the stack
Coming off the heap
Preinitialized memory (like constants or strings embedded in the
source)

If it is coming off the stack then you just add "12" to the pointer to
the top of your stack. True, it might be spending time making sure to
zero that memory.

If it is coming off the heap then there might be a speed hit. And I
suppose it is up to the compiler to determine how it wants to allocate
memory for your local variables...but I would very much guess it's not
allocating from the heap.

If it is preinitialized then the value is just sitting there in memory
and there isn't any allocation during runtime (but rather when the
application was loaded.)

But as was stated before, I would recommend trying both ways and timing
them to see what the difference is. But even if it is faster, I would
note that the first rule of optimization is to not pre-optimize! In the
end 90 of what makes your code go slow will be a bad algorithm that
needs to be cleaned up. Getting an extra 0.1% off the rest of your code
will not only be worth it, but can make the chances for bugs to happen
increase since optimized code will generally not be the most
programming-safe-type code. Best to keep the scope of all variables as
small as you can get it.
 
C

Chris Croughton

My simplistic mind tells me that having local variables within looping
constructs is a bad idea. The reason is that these variables are
created during the beginning of an iteration and deleted at the end of
the iteration. Kindly note that I am not talking about dynamically
allocating memory within a loop, which is a perfectly valid operation
(albeit with a chance of resulting in a memory leak, unless we are
careful).

It depends on the cost of 'creating' such a variable. For a simple
variable like an int there may well be no cost at all. For instance, on
one compiler I know the constructs:

void func(void)
{
int i;
for (i = 0; i < 10; ++i)
{
int j;
...
}
...
for (i = 0; i < 10; ++i)
{
int k;
...
}
}

will result in a stack structure

space for i
space for j and k (shared)

allocated when the function is entered.
The most common justification that I have heard for local variables
within looping constructs is that it helps localize the scope of the
variables. But isn't there another way to accomplish the same thing
----- by declaring a block outside the looping construct and declaring
local variables within that? Here's what I mean:

{
char buf1[100];
for(int i = ...;...;)

That isn't within the loop, though, that is exactly equivalent to

{
int i;
for (i = initial; condition; stuff)
...
}

This is true in both C (C99 standard) and C++, they are defined to be
identical.

It is also identical to

{
int i;
i = initial;
while (condition)
{
...
stuff;
}
}
I have never seen anyone propose this approach. Are there any
drawbacks that I am not seeing?

Clarity. Reducing the abount of nesting of braces and keeping
variables local to where they are used inproves clarity and reduces the
possibilities of mistakes when the code is edited (inadvertently using a
variable used in an outer loop for example, if you always declare your
loop variable when it is wanted and it is always removed after that then
it minimises the risk).

Chris C (note followups to c.l.c only)
 
R

Richard Bos

Chris Williams said:
That would be my guess. So far as I am aware all memory that you use
will either be:

Coming off the stack
Coming off the heap
Preinitialized memory (like constants or strings embedded in the
source)

Well, yes and no. _Most_ memory you use in _most_ current desktop
environments will do so. But the Standard does not require any memory to
come from any kind of structure - all it requires is that memory you get
from malloc() behaves a certain way, and memory assigned to int a[14]
behaves another way, and so forth.

Of course, this makes your final point - that there's no way to know
which is faster - only more true.

Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top