[comp.std.c] Initialization of automatics within loops

geoff@locus.com (Geoff Kuenning) (02/05/91)

The following little program demonstrates a problem found in the XSend()
routine of XlibInt.c:

	main ()
	    {
	    int i = 0;
	    while (i < 3)
		{
		int j = 4;
		printf ("i = %d, j = %d\n", i, j);
		i++;
		j++;
		}
	    }

What does this print?  On the machine I'm using, it prints the same value
for j (j = 4) three times.  On the other hand, the programmer of XSend()
clearly expected three different values for j.

I just checked the ANSI C spec on this, and found it unclear.  It is
explicitly stated that automatics are initialized on every entry to a
compound statement.  However, it is not made clear whether the construct:

	while (<expression>)
	    <statement>

is considered to *re-enter* the compound statement every time or not.  My
instinct says yes, the statement is re-entered, but obviously this
interpretation is open to discussion.  In terms of usefulness, I could
argue that either option is useful to programmers.

What do people think the spec should do about this question?  (BTW, the
spec explicitly says that implementations get to decide whether automatics
are initialized upon jumps into a compound statement, so perhaps this
issue should also be implementation-defined).

I'm cross-posting to comp.windows.x so that X-Windows users will be made
aware of a potential bug (the problem will show up only under certain
very rare conditions, so it could be lurking in your system without your
knowledge).
-- 

	Geoff Kuenning	geoff@la.locus.com	geoff@ITcorp.com

bhoughto@hopi.intel.com (Blair P. Houghton) (02/06/91)

In article <1991Feb5.023809.389086@locus.com> geoff@locus.com (Geoff Kuenning) writes:
>The following little program demonstrates a problem found in the XSend()
>routine of XlibInt.c:
>
>	main ()
>	    {
>	    int i = 0;
>	    while (i < 3)
>		{
>		int j = 4;
>		printf ("i = %d, j = %d\n", i, j);
>		i++;
>		j++;
>		}
>	    }

Digression:  does anyone know where this style (having the
statements aligned with the braces that enclose them) came
from?  Before a few months ago I'd never seen it, and now
I've seen it in a dozen programs.  Who's fomenting this
bletcherous bowdlerization of my beloved C?

>I just checked the ANSI C spec on this, and found it unclear.  It is
>explicitly stated that automatics are initialized on every entry to a
>compound statement.  However, it is not made clear whether the construct:
>
>	while (<expression>)
>	    <statement>
>
>is considered to *re-enter* the compound statement every time or not.  My
>instinct says yes, the statement is re-entered, but obviously this
>interpretation is open to discussion.  In terms of usefulness, I could
>argue that either option is useful to programmers.

You answer your own question by your example.  A statement
is a statement is a statement, and the `while' has no clue
whether the statement is compound or simple; it merely
transfers control to it when the conditional-expression
evaluates nonzero, and takes back control when its end is
reached.  Any auto variables within the compound statement
can be reallocated as the implementation desires, and must
be reinitialized.

In the description of the compound statement:
"The initializers of objects that have automatic storage duration
are evaluated and the values are stored in the objects..."
(ANSI X3.159-1989, sec. 3.6.2, p. 77, ll. 4-5)

>What do people think the spec should do about this question?

At most, add the definition of the term "entry" to section
1.6, Definition of Terms; although that'd be a bit of overkill.
It might even be too much to ask for a footnote where that
use of the word appears (see below).

>(BTW, the
>spec explicitly says that implementations get to decide whether automatics
>are initialized upon jumps into a compound statement,

No it doesn't.

In sec. 3.1.2.4, Storage Duration of Objects, it explains
automatic storage, guarantees that the object will exist
whether you get into the statement through "...normal entry
to the block with which it is associated, or on a jump from
outside the block to a labeled statement in the block..."
(Ibid, sec. 3.1.2.4, p. 23, l. 14), then says:  "If an
initialization is specified for the value stored in the
object, it is performed on each normal entry, but not if
the block is entered by a jump to a labeled statement"
(Ibid, ll. 16-18).

Ergo, jump to a labeled statement, and the initialization is skipped.

(Without a labeled statement, you can't get there from here).

Note that in:

	/* just a block to scope-limit foo and bar */
	{
	    int foo = 3;
mickle:
	    int bar = 0111;

	    foo *= bar;
	}

`mickle:' labels `foo *= bar;', which is a statement,
rather than `int bar = 0111;', which is a declaration, not
a statement.  So, despite the fact that `int bar = 0111;'
appears after `mickle:', it is guaranteed that neither
initialization is performed.

>I'm cross-posting to comp.windows.x so that X-Windows users will be made
>aware of a potential bug (the problem will show up only under certain
>very rare conditions, so it could be lurking in your system without your
>knowledge).

It (the character of the bug, not this particular bug)
does explain a few things I noticed about some commercial
implementations of the X-servers (from a company who shall
remain nameless), most notably the every-other-X-server bugs,
where you'd get odd alterations to X defaults on every
other time you reran the server (it dies and restarts with
every console-window logout).  The most common one was that
the screen-blanking would be reset to 0 seconds.  Very
annoying.  There were others involving massive destruction
of the X error logging file when one tried to get fonts
using certain _legal_ font-naming patterns.  It left many
megabytes of nulls in the error log, clogging the root
partition.  Then it dumped 2mb of X-server core in there,
too, which stuck despite the clog...

				--Blair
				  "Although their initials are DEC."

gwyn@smoke.brl.mil (Doug Gwyn) (02/06/91)

In article <1991Feb5.023809.389086@locus.com> geoff@locus.com (Geoff Kuenning) writes:
-	    while (i < 3)
-		{
-		int j = 4;
-		printf ("i = %d, j = %d\n", i, j);
-		i++;
-		j++;
-		}
-What does this print?  On the machine I'm using, it prints the same value
-for j (j = 4) three times.  On the other hand, the programmer of XSend()
-clearly expected three different values for j.

The programmer clearly made a mistake.  j has the value 4 for each
execution of the printf().  I was unable to tell from the code
snippet just what the programmer intended; merely changing the
storage class of j to "static" would probably not be correct either.

-I just checked the ANSI C spec on this, and found it unclear.  It is
-explicitly stated that automatics are initialized on every entry to a
-compound statement.  However, it is not made clear whether the construct:
-	while (<expression>)
-	    <statement>
-is considered to *re-enter* the compound statement every time or not.

There is no question whatsoever about this.  The compound statement is
completely evaluated for each iteration; this includes the auto
initialization.

blodgett@apollo.HP.COM (Bruce Blodgett) (02/06/91)

In article <2293@inews.intel.com> bhoughto@hopi.intel.com (Blair P. Houghton) writes:
> Note that in:
> 
> 	/* just a block to scope-limit foo and bar */
> 	{
> 	    int foo = 3;
> mickle:
> 	    int bar = 0111;
> 
> 	    foo *= bar;
> 	}
> 
> `mickle:' labels `foo *= bar;', which is a statement,
> rather than `int bar = 0111;', which is a declaration, not
> a statement.  So, despite the fact that `int bar = 0111;'
> appears after `mickle:', it is guaranteed that neither
> initialization is performed.

Section 3.6.1 (Labeled Statements) provides relevant syntax:
labeled-statement:
        identifier : statement

As does Section 3.6.2 (Compound Statement, or Block):
compound-statement:
        { opt-declaration-list opt-statement-list }

Your declarations of foo and bar make up the optional
declaration-list.  foo *= bar; makes up the optional statement-list.
The label mickle: precedes a declaration, not a statement.  This is
not syntactically legal.  There is not a question of whether to assign
0111 to bar "if the block is entered by a jump to a labeled
statement", because you have not provided a legal code fragment.
Bruce Blodgett
blodgett@apollo.hp.com
(508) 256-0176 x4037

rns@tortuga.SanDiego.NCR.COM (Rick Schubert) (02/07/91)

In article <2293@inews.intel.com> bhoughto@hopi.intel.com (Blair P. Houghton) writes:
>In article <1991Feb5.023809.389086@locus.com> geoff@locus.com (Geoff Kuenning) writes:
>>(BTW, the
>>spec explicitly says that implementations get to decide whether automatics
>>are initialized upon jumps into a compound statement,

>No it doesn't.

>Ergo, jump to a labeled statement, and the initialization is skipped.

>Note that in:

>	/* just a block to scope-limit foo and bar */
>	{
>	    int foo = 3;
>mickle:
>	    int bar = 0111;
>
>	    foo *= bar;
>	}

>                         it is guaranteed that neither
>initialization is performed.

Someone else answered that the label is invalid here, so I'll proceed as
if the label were after the declarations.  In this case it CANNOT be
guaranteed that the initialization is NOT performed since there is no way
to tell (in a Standard Conforming program) that the initialization was or
was not done.  There is no way to test for an uninitialized value.  The only
thing that would be invalid for the implementation (compiler) to do would
be to evaluate an initialization expression that had side-effects that could
be tested for.

-- Rick Schubert (rns@tortuga.SanDiego.NCR.COM)

bhoughto@hopi.intel.com (Blair P. Houghton) (02/08/91)

In article <4fa77adf.20b6d@apollo.HP.COM> blodgett@apollo.HP.COM
(Bruce Blodgett) writes:
>In article <2293@inews.intel.com> bhoughto@hopi.intel.com (Blair P. Houghton) writes:
>> mickle:
>> 	    int bar = 0111;
>> 	    foo *= bar;
>> `mickle:' labels `foo *= bar;', which is a statement,
[...incrimination deleted...]
>The label mickle: precedes a declaration, not a statement.  This is
>not syntactically legal.

Oops.  Good thing I don't code that way...

				--Blair
				  "Stop laughing, Doug."

tom@flood.com (Tom Chatt) (02/12/91)

In article <1991Feb5.023809.389086@locus.com> geoff@locus.com (Geoff Kuenning) writes (paraphrases in brackets):
> [ In the following example (drawn from actual Xlib code): ]
>
>	main ()
>	    {
>	    int i = 0;
>	    while (i < 3)
>		{
>		int j = 4;
>		printf ("i = %d, j = %d\n", i, j);
>		i++;
>		j++;
>		}
>	    }
>
> [ Does the initialization of j=4 occur at the start of each iteration
>   of the compound statement, or only the first iteration? Mr. Kuenning's
>   compiler sets j=4 each time; the X programmer evidently expected otherwise.
>   The ANSI C standard says that automatics are initialized on every entry
>   to a compound statement, but does the above example count as three
>   entries to the compound statement, or only one? ]

WRT entering the compound statement, it seems pretty clear to me that
the compound statement is re-entered at each iteration, and that the
initialization (as per spec) should occur each time. I say this because
the test "i < 3" is clearly outside of the compound statement, and this
executes at each iteration, thus you must have exited the compound statement.

Though a compiler *may* optimize this out, it would be perfectly
correct for a compiler to *allocate* the automatic variable at each
entry to the compound statement. In the example above, the programmer
has some expectation about the value of the automatic variable persisting
outside of its scope (i.e., between iterations). This practice is
extremely dubious and inadvisable. If a programmer expects a variable's
value to persist outside of its scope, he should declare it as static.
-- 
Tom Chatt                        \   Don't take offense, take action.
Internet: tom@flood.com           \    Speak up. When we remain silent,
UUCP: ...!uunet!flood!tom        / \     we oppress ourselves.