scoping within for loops

Discussion in 'C Programming' started by Stephen J. Fromm, Jun 26, 2003.

  1. What are the scoping rules for a for loop?

    Example:

    int i;
    for(i = 0; i < 10; i++) {
    int j;

    /* more code here */

    }

    In this example, is the scope (a) "renewed" upon each iteration of
    loop, or (b)common across all iterations? (My impression from
    compiling code is "(a)").

    This is in reference to scoping within blocks: one can write, absent
    any control structure,
    {
    int j = 7;
    /* more code */
    }
    and the lifetime of j is confined to the { }s, if I recall correctly.
    With this in mind, (a) is like
    int i = 0;
    {
    int j;
    /* more code */
    }
    /* test i */
    {
    int j; /* New "instance" of j */
    /* more code (again) */
    }
    ....

    whereas (b) is like
    int i = 0;
    {
    int j; /* only 1 instance of j */
    /* more code */

    /* test i */

    /* more code */
    }

    TIA,

    sjfromm
    Stephen J. Fromm, Jun 26, 2003
    #1
    1. Advertising

  2. Stephen J. Fromm

    Derk Gwen Guest

    (Stephen J. Fromm) wrote:
    # What are the scoping rules for a for loop?
    #
    # Example:
    #
    # int i;
    # for(i = 0; i < 10; i++) {
    # int j;
    #
    # /* more code here */
    #
    # }
    #
    # In this example, is the scope (a) "renewed" upon each iteration of
    # loop, or (b)common across all iterations? (My impression from
    # compiling code is "(a)").

    j is local to the surrounding {...}. It's value is undefined from the
    beginning of the block until initialised or assigned a value. That's
    all you should really concern yourself with.

    for (...)
    if (...) {
    int j;
    }else {
    float k;
    }

    It is possible j and k will be assigned the same place in the stack frame.
    With each iteration that slot in the stack frame might hold the previous
    value of j, of k, or of anything else.

    # and the lifetime of j is confined to the { }s, if I recall correctly.
    # With this in mind, (a) is like
    # int i = 0;
    # {
    # int j;
    # /* more code */
    # }
    # /* test i */
    # {
    # int j; /* New "instance" of j */
    # /* more code (again) */
    # }

    In non-iterative code, the variables in disjoint blocks may be overlapped or
    disjoint at the compiler's discretion. There no universally correct answer.
    Within a loop a compiler will assign a variable to the same stack frame offset,
    but that's for a practical reason: otherwise the stack frame could grow
    with each iteration and overflow the stack when there is no need to do so.
    It's not something you should depend on though, since other code might use
    the same slot without warning you.

    --
    Derk Gwen http://derkgwen.250free.com/html/index.html
    Raining down sulphur is like an endurance trial, man. Genocide is the
    most exhausting activity one can engage in. Next to soccer.
    Derk Gwen, Jun 27, 2003
    #2
    1. Advertising

  3. Stephen J. Fromm wrote:

    > What are the scoping rules for a for loop?
    >
    > Example:


    > cat main.c

    #include<stdio.h>

    int main(int argc, char* argv[]) {
    const int n = 10;
    for (int i = 0; i < n; ++i) {
    fprintf(stdout, "%d = i\n", i);
    int i = 0;
    fprintf(stdout, "%d = i\n", i);
    }
    return 0;
    }

    > gcc -Wall -std=c99 -pedantic -o main main.c
    > ./main

    0 = i
    0 = i
    1 = i
    0 = i
    2 = i
    0 = i
    3 = i
    0 = i
    4 = i
    0 = i
    5 = i
    0 = i
    6 = i
    0 = i
    7 = i
    0 = i
    8 = i
    0 = i
    9 = i
    0 = i
    E. Robert Tisdale, Jun 27, 2003
    #3
  4. "E. Robert Tisdale" <> wrote in message news:<>...
    > Stephen J. Fromm wrote:
    >
    > > What are the scoping rules for a for loop?
    > >
    > > Example:

    >
    > > cat main.c

    > #include<stdio.h>
    >
    > int main(int argc, char* argv[]) {
    > const int n = 10;
    > for (int i = 0; i < n; ++i) {
    > fprintf(stdout, "%d = i\n", i);
    > int i = 0;
    > fprintf(stdout, "%d = i\n", i);
    > }
    > return 0;
    > }


    Thanks for your reply.

    Very interesting example. While the "first i" and "second i" occur
    within the same block, your example makes it appear there are is no
    scoping conflict. Is this because
    (a) scoping is largely dependent on declarations, and declarations
    only extend downwards (here, the declarations *don't* occur in the
    same block, and the "second i" is declared after the first one is used
    but not declared), or
    (b) gcc doesn't conform to some standard,
    (c) c99 is different on this point than ANSI C (I guess that's c89 but
    am not really sure.

    Cheers,

    S

    >
    > > gcc -Wall -std=c99 -pedantic -o main main.c
    > > ./main

    > 0 = i
    > 0 = i
    > 1 = i
    > 0 = i
    > 2 = i
    > 0 = i
    > 3 = i
    > 0 = i
    > 4 = i
    > 0 = i
    > 5 = i
    > 0 = i
    > 6 = i
    > 0 = i
    > 7 = i
    > 0 = i
    > 8 = i
    > 0 = i
    > 9 = i
    > 0 = i
    Stephen J. Fromm, Jun 27, 2003
    #4
  5. Stephen J. Fromm

    Eric Sosman Guest

    wrote:
    >
    > Stephen J. Fromm <> wrote:
    > >
    > > In this example, is the scope (a) "renewed" upon each iteration of
    > > loop, or (b)common across all iterations? (My impression from
    > > compiling code is "(a)").

    >
    > Correct. The loop body is entered and exited each time the loop
    > repeats, so any variables that are declared within the loop body come
    > into scope and go out of scope on each iteration.


    More precisely, the scope of an identifier is static:
    it is a consequence of the lexical structure of the program,
    not of its behavior at run-time. The "scope" of an identifier
    is the region of the code in which that identifier is associated
    with a particular purpose -- its binding, if you will.

    If an identifier refers to a data object (as opposed to,
    say, an `enum' constant), we can also ask about the "storage
    duration" of the object -- its "lifetime." When execution
    enters a {}-enclosed block, the data objects for any `auto'
    or `register' variables belonging to that block come into
    existence. They remain in existence until execution leaves
    the block, at which point they return to the limbo whence
    they came. If the block is re-entered, new data objects are
    created for the new execution; they may (or may not) occupy
    the same memory locations as the data objects from the first
    execution, but in every essential they are brand-new objects,
    independent of those that existed the first time around.

    An easy way to observe this independence is through
    recursion:

    void f(unsigned int depth) {
    auto /* for emphasis */ int variable;
    printf ("Entering level %u: `variable' is at %p\n",
    depth, (void*) &variable);
    if (depth > 0)
    f(depth - 1);
    printf ("Leaving level %u: `variable' is at %p\n",
    depth, (void*) &variable);
    }

    Call this as `f(3)', say, and you'll easily see that the
    identifier `variable' refers to different objects at different
    executions of the {}-enclosed block that is the function body.

    Back to the original question about a `for' loop (`while'
    and `do' loops behave exactly the same way) of the form

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

    The scope of the identifier `j' extends from its declaration
    to the ending } -- this is the static code region during which
    the binding of `j' to a particular data object exists. The
    ten executions of the loop body create and destroy ten data
    objects for `j' to refer to. It will often turn out that the
    ten objects will all occupy the same memory location or register,
    but they are different objects nonetheless because their lifetimes
    do not overlap. In particular, it is *not* guaranteed that a
    value assigned to `j' in one iteration will still be intact
    when the next iteration begins.

    --
    Eric Sosman, Jun 27, 2003
    #5
  6. Stephen J. Fromm

    Chris Torek Guest

    >> Stephen J. Fromm wrote:
    >> > What are the scoping rules for a for loop?


    >"E. Robert Tisdale" <> wrote in message news:<>...
    >> > cat main.c

    >> #include<stdio.h>
    >>
    >> int main(int argc, char* argv[]) {
    >> const int n = 10;
    >> for (int i = 0; i < n; ++i) {
    >> fprintf(stdout, "%d = i\n", i);
    >> int i = 0;
    >> fprintf(stdout, "%d = i\n", i);
    >> }
    >> return 0;
    >> }


    In article <>
    Stephen J. Fromm <> writes:
    >Very interesting example. While the "first i" and "second i" occur
    >within the same block, your example makes it appear there are is no
    >scoping conflict. Is this because
    >(a) scoping is largely dependent on declarations, and declarations
    >only extend downwards (here, the declarations *don't* occur in the
    >same block, and the "second i" is declared after the first one is used
    >but not declared), or
    >(b) gcc doesn't conform to some standard,
    >(c) c99 is different on this point than ANSI C (I guess that's c89 but
    >am not really sure.


    It is indeed an interesting example, and the answers to your three
    questions are "yes (depending on what you mean by `largely')",
    "no", and "no" :) (Not that gcc, even with -std=c99, quite conforms
    to C99 -- but any nonconformance is not the reason the example
    works. Further, "ANSI C" can be either or both of both C89 and
    C99, depending on who is saying the words.)

    According to the draft C99 standard I have handy in text form:

    Except for the behavior of a continue statement in the
    loop body, the statement

    for ( clause-1 ; expr-2 ; expr-3 ) statement

    and the sequence of statements

    {
    clause-1 ;
    while ( expr-2 ) {
    statement
    expr-3 ;
    }
    }

    are equivalent ....

    We are also told that:

    Two identifiers have the same scope if and only if their
    scopes terminate at the same point.

    Applying the prescribed transformation to the loop:

    for (int i = 0; i < n; i++)
    { ... }

    produces:

    {
    int i = 0;
    while (i < n) {
    { ... }
    i++;
    }
    }

    Note that we now have not one, nor even two, but THREE (!) sets of
    scope-affecting "{}"s. The "for" loop's declaration of identifier
    i occurs inside the outermost {}s and terminates at the final }.
    The second declaration of "i" -- in the "..." part -- occurs inside
    the innermost {}s and terminates at the first }. These two "i"s
    thus have different scopes, and can (and do) name different objects.

    The C99 standard's expansion, in which a "for" loop has at least
    two implied "{"s, is the direct answer to your original question.
    --
    In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://67.40.109.61/torek/index.html (for the moment)
    Reading email is like searching for food in the garbage, thanks to spammers.
    Chris Torek, Jun 27, 2003
    #6
  7. Stephen J. Fromm

    Micah Cowan Guest

    (Stephen J. Fromm) writes:

    > "E. Robert Tisdale" <> wrote in message news:<>...
    > > Stephen J. Fromm wrote:
    > >
    > > > What are the scoping rules for a for loop?
    > > >
    > > > Example:

    > >
    > > > cat main.c

    > > #include<stdio.h>
    > >
    > > int main(int argc, char* argv[]) {
    > > const int n = 10;
    > > for (int i = 0; i < n; ++i) {
    > > fprintf(stdout, "%d = i\n", i);
    > > int i = 0;
    > > fprintf(stdout, "%d = i\n", i);
    > > }
    > > return 0;
    > > }

    >
    > Thanks for your reply.
    >
    > Very interesting example. While the "first i" and "second i" occur
    > within the same block, your example makes it appear there are is no
    > scoping conflict. Is this because
    > (a) scoping is largely dependent on declarations, and declarations
    > only extend downwards (here, the declarations *don't* occur in the
    > same block, and the "second i" is declared after the first one is used
    > but not declared), or
    > (b) gcc doesn't conform to some standard,
    > (c) c99 is different on this point than ANSI C (I guess that's c89 but
    > am not really sure.


    C99 *is* ANSI C--or more accurately, it is ISO C. It is the newer
    standard.

    C99 is *very* different on this point than C89, as in C89 both of the
    above declarations of i are illegal. In the first case, declarations
    are not allowed in the first clause of a for statement; in the second,
    declarations must precede all statements at the same block level.

    The answer to your question is: none of the above. The reason is that
    the first i and second i *don't* occur within the same
    block. Declarations occuring in the first clause of a for statement
    exist in a block beginning right there, and extending to the end of
    the statement (the end of the for loop). The second declaration,
    however, exists within a separate, inner block which begins with the
    opening { brace, and ends, along with the outer block, with the }
    brace.

    -Micah
    Micah Cowan, Jun 28, 2003
    #7
  8. Stephen J. Fromm

    CBFalconer Guest

    Micah Cowan wrote:
    > (Stephen J. Fromm) writes:
    > > <> wrote:
    > >

    .... snip ...
    > > >
    > > > > cat main.c
    > > > #include<stdio.h>
    > > >
    > > > int main(int argc, char* argv[]) {
    > > > const int n = 10;
    > > > for (int i = 0; i < n; ++i) {
    > > > fprintf(stdout, "%d = i\n", i);
    > > > int i = 0;
    > > > fprintf(stdout, "%d = i\n", i);
    > > > }
    > > > return 0;
    > > > }

    > >

    .... snip ...
    >
    > The answer to your question is: none of the above. The reason
    > is that the first i and second i *don't* occur within the same
    > block. Declarations occuring in the first clause of a for
    > statement exist in a block beginning right there, and extending
    > to the end of the statement (the end of the for loop). The
    > second declaration, however, exists within a separate, inner
    > block which begins with the opening { brace, and ends, along
    > with the outer block, with the } brace.


    Which is just the sort of thing that makes parsing C such a bear,
    and impedes really serious complete description in BNF terms.
    That means that a for statement needs to be described by something
    like:

    for-statement = 'for' <for-block>
    for-block = '(' <for-header> ')' <statement-block>

    to provide syntactical hooks for permissible declarations. It
    gets worse.

    --
    Chuck F () ()
    Available for consulting/temporary embedded and systems.
    <http://cbfalconer.home.att.net> USE worldnet address!
    CBFalconer, Jun 28, 2003
    #8
  9. Stephen J. Fromm

    Len Budney Guest

    (Stephen J. Fromm) wrote:
    >
    > I assume that's consistent with the example another poster (Tisdale)
    > gave?:
    >
    > for (int i = 0; i < n; ++i) {
    > fprintf(stdout, "%d = i\n", i);
    > int i = 0;
    > fprintf(stdout, "%d = i\n", i);
    > }
    >
    > His example treats the two instances of i differently (as if the
    > second were "j", not "i"). I'm assuming this is because "It's value
    > is undefined from the beginning of the block until initialised or
    > assigned a value."


    No. At least, poorly articulated. The scope of the first 'i' is the
    for statement and the dependent block--i.e., larger than the block.
    The scope of the second 'i' is from its declaration to the end of the
    block. The second 'i' hides the first 'i' within its scope--but at the
    bottom of the block, the second 'i' passes out of scope, the loop
    counter is exposed, and the loop condition is tested correctly.

    Also note that the second 'i' behaves differently than if it were 'j',
    in that the loop variable cannot be accessed from within the scope of
    the second 'i'.

    Some compilers, that don't implement C99 correctly (including VC++),
    would treat the loop variable as having scope equal to the block
    containing the for statement, and would disallow a loop later in the
    block using 'i' as its counter.

    > By "discretion", "no correct answer," "practical reason," "don't
    > depend on it," I assume you mean there's nothing in the standards to
    > make it go the way I think it goes.


    Indubitably. Once a variable passes out of scope, its location may
    contain the last value, another variable's value--in fact, anything.
    With optimization turned on, it's very likely to contain something
    else.

    --Len.
    Len Budney, Jul 10, 2003
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ian McMeans

    scoping with lambda in loops

    Ian McMeans, Sep 16, 2003, in forum: Python
    Replies:
    8
    Views:
    342
    Jacek Generowicz
    Sep 17, 2003
  2. Replies:
    23
    Views:
    623
    Joachim Schmitz
    Jul 1, 2008
  3. walterbyrd
    Replies:
    16
    Views:
    471
    Steven D'Aprano
    Dec 18, 2008
  4. Interrupt

    problems with logic operations within loops

    Interrupt, Jan 26, 2010, in forum: C Programming
    Replies:
    46
    Views:
    947
    Phil Carmody
    Jan 30, 2010
  5. Me
    Replies:
    2
    Views:
    243
Loading...

Share This Page