comma in a c statement, ie for(i=0,i2=0;i<10;i++,i2)

N

Nick Keighley

As soon as you need a break or continue in the for loop, my
heuristic is to go with a while. But I'm not always consistent
about it...

I use these to signal intent. for (i= 0; i < 10; i++) signals that
it's an "ordinary" loop that executes 10 times.

while(x > 10) signals something a bit more complicated is going on

and for(;;) indicates something rather tricksy probably involving a
break (and I'll often put a distinctive comment in at the break).
 
B

Ben Bacarisse

Shao Miller said:
With _much_ apology, I've assumed that N1256.PDF's 6.2.4p6 is C99's.
I am referring to C99's VLAs, which obviously introduce a number of
quirks (relative to C89/C90). One of those quirks is that their
lifetime and scope are fairly closely tied, and it's relevant for a
for' loop's clause-1. (_Why_ one would declare a VLA in a 'for' loop
is a matter of practicality, but _that_ one can is a matter of fact.)

Or, I could be missing something.

The "for" and the "while" versions are the same with respect to
lifetime, with or without VLAs. The loop pattern you commented on had
<initialise> but you decided to make a point about the fact that C99
also permits a declaration there. That's fine, but your point is still
wrong. You must then compare

for (<declaration>; <condition>;) {
<code that uses continue>
<increment>
}

with
<declaration>;
while (<condition>) {
<code that uses continue>
<increment>
}

which do not differ significantly in the required lifetime of the
declared object.
 
S

Shao Miller

The "for" and the "while" versions are the same with respect to
lifetime, with or without VLAs. The loop pattern you commented on had
<initialise> but you decided to make a point about the fact that C99
also permits a declaration there. That's fine, but your point is still
wrong. You must then compare

for (<declaration>; <condition>;) {
<code that uses continue>
<increment>
}

with
<declaration>;
while (<condition>) {
<code that uses continue>
<increment>
}

which do not differ significantly in the required lifetime of the
declared object.

How do you "undeclare" the identifier(s) in the 'while' version, after
the 'while' statement, so as to free up storage? Do you _need_ to free
the storage for the identifier(s) in the 'for' version?

It's also a matter of namespace pollution. Please consider:

#include <stddef.h>
#define Countof(array) (sizeof (array) / sizeof *(array))

int finally(size_t * arr) {
(void) arr;
return 0;
}

int main(void) {
int n = 42;
int i = 13;

for (size_t i = 0, arr[n]; i < Countof(arr) || finally(arr); ++i)
arr = i;

int x = i;
(void) x;

return 0;
}

What value does 'x' land with? Is the storage for 'arr' required by the
time 'x' is initialized? It seems to me that with a 'while', you'd need
to do some curly brace wrapping.

- Shao Miller
 
B

Ben Bacarisse

Shao Miller said:
How do you "undeclare" the identifier(s) in the 'while' version, after
the 'while' statement, so as to free up storage? Do you _need_ to
free the storage for the identifier(s) in the 'for' version?

I don't think these questions help. I think the difference in opinion
may come from the interpretation of a footnote. We both agree that the
scope of a for-loop-declared VLA object extends from the declaration to
the end of the controlled statement (that's your first cited
paragraph). The second citation says:

For such an object [of automatic storage duration] that does have a
variable length array type, its lifetime extends from the declaration
of the object until execution of the program leaves the scope of the
declaration.[27]

Footnote 27 is about what it means to leave the scope:

[27] Leaving the innermost block containing the declaration, or
jumping to a point in that block or an embedded block prior to
the declaration, leaves the scope of the declaration.

Presumably you take falling out of the for loop as "leaving the scope"
but that does not fit the wording of the footnote.

Maybe falling out of the scope *is* "leaving the scope" (i.e. maybe
footnote 27 is there to illustrate a couple of possibilities, rather
than explain the term). I agree that that's perfectly reasonable, but
there are other cases where going from being in the scope to not being
in it is not the same as leaving it (function calls for example).

On your side, there is evidence that even if footnote 27 is intended to
explain the term, it might simply fail to consider the case of
identifiers declared in for loops. The evidence being that that occurs
elsewhere. For example, 6.4.1p4 says:

If the declarator or type specifier that declares the identifier
appears inside a block or within the list of parameter declarations in
a function definition, the identifier has block scope, which
terminates at the end of the associated block.

Presumably, 6.8.5.3p1 refines this statement, but it would be better if
the above at least mentioned the case of a for loop.

I am leaning you way here. I suspect the footnote 27 just lists a
couple of possibilities and the natural meaning of "leaving the scope"
applies here, but I am glad it's a debate about a useless case.

<snip>
 
K

Keith Thompson

I was quoting from N1570, the C11 draft; sorry if I didn't make that
clear. The paragraph numbering of section 6.2.4 changed from C99 to
C11. N1570 6.2.4p6 is C99 and N1256 6.2.4p5.

Apart from the renumbering, the paragraph discussing non-VLA objects is
word-for-word identical between C99 and N1256; N1570 adds the phrase "or
compound literal".

The following paragraph, discussing VLA objects (which is *not* what I
was discussing) is word-for-word identical in C99, N1256, and N1570.
I must _sincerely_ apologize. This must be a circumstance where
N1256.PDF differs from C99. Please forgive me for referring to the
wrong point! N1256.PDF's 6.2.4p6:

"For such an object that does have a variable length array type, its
lifetime extends from the declaration of the object until execution of
the program leaves the scope of the declaration.27) If the scope is
entered recursively, a new instance of the object is created each time.
The initial value of the object is indeterminate."
[...]

Because VLAs are the predominant feature for C99 from my perspective, I
realize now that I've actually skipped mentioning them, but they are
precisely what I'm referring to.

Hmm. I consider VLAs to be just one of a number of features added by
C99, certainly not the predominant one. Until now, I hadn't even
considered them in the context of this discussion.

Perhaps you could briefly restate the point you were making?
 
K

Keith Thompson

James Kuyper said:
Keith said:
[...]
A key point to understand is that commas are NOT always part of a comma
expression. The parameters of a function declaration and the arguments
of a function call are separated by commas, and the same is true for
function-like macros. Commas separate the initializers for the elements
of an array or members of a structure. A single declaration can declare
multiple declarators, separated by commas, as in "int a,b;". Commas are
used to separate the members of a union or enumeration.

How do commas separate the members of a union?

union u {
int x, y;
};

:)

More useful:

union u {
int x, *y;
};

Even better:

union u {
int val, ref[1];
};

I would, of course, seriously berate anyone who did that in production
code.
 
S

Shao Miller

I was quoting from N1570, the C11 draft; sorry if I didn't make that
clear. The paragraph numbering of section 6.2.4 changed from C99 to
C11. N1570 6.2.4p6 is C99 and N1256 6.2.4p5.

Aha.

Apart from the renumbering, the paragraph discussing non-VLA objects is
word-for-word identical between C99 and N1256; N1570 adds the phrase "or
compound literal".

Ah yes, compound literals get a little more consideration, there.
[...]


Hmm. I consider VLAs to be just one of a number of features added by
C99, certainly not the predominant one. Until now, I hadn't even
considered them in the context of this discussion.

Perhaps you could briefly restate the point you were making?

Just that using a 'for' instead of a 'while' when, at first glance, it
might seem that either would do nicely, have some subtle differences
regarding memory usage, for VLAs. Hand-waving the use of 'for' instead
of 'while' dismisses those subtleties.

The code example I recently offered to Ben B. was:

#include <stddef.h>
#define Countof(array) (sizeof (array) / sizeof *(array))

int finally(size_t * arr) {
(void) arr;
return 0;
}

int main(void) {
int n = 42;
int i = 13;

for (size_t i = 0, arr[n]; i < Countof(arr) || finally(arr); ++i)
arr = i;

int x = i;
(void) x;

return 0;
}

You might notice the suitability of this 'for' for a macro or for
volatile 'n' (evaluating it only once). There is no equivalent 'while'
that I'm aware of where the lifetime (and thus the storage requirement)
of 'arr' ends after the iteration statement. I think you'd need to
surround a 'while' counterpart with a block and some declarations, which
can have an aesthetic drawback for certain indentation conventions:

#include <stddef.h>
#define Countof(array) (sizeof (array) / sizeof *(array))

int finally(size_t * arr) {
(void) arr;
return 0;
}

int main(void) {
int n = 42;
int i = 13;

{
size_t i = 0;
size_t arr[n];

while (i < Countof(arr)) {
arr = i;
++i;
}
finally(arr);
}

int x = i;
(void) x;

return 0;
}

Please also consider a macro:

#define for_macro(n) \
for (size_t i = 0, arr[n]; i < Countof(arr) || finally(arr); ++i)

and how you can do:

for_macro(42)
statement;

But using 'while' could be trickier to "inject" the 'statement' into the
enclosing block of the 'while':

#define while_macro(n, statement) \
do { \
size_t i = 0; \
size_t arr[n]; \
\
while (i < Countof(arr)) { \
statement; \
++i; \
} \
finally(arr); \
} while (0)

/* Example usage */
while_macro(42, arr = i);

and where the statement is itself an iteration, selection, or compound
statement, it gets uglier.

These are obviously contrived and, as Ben B. mentioned, most likely a
"useless case."

- Shao Miller
 
T

Tim Rentsch

Ben Bacarisse said:
The "for" and the "while" versions are the same with respect to
lifetime, with or without VLAs. The loop pattern you commented on had
<initialise> but you decided to make a point about the fact that C99
also permits a declaration there. That's fine, but your point is still
wrong. You must then compare

for (<declaration>; <condition>;) {
<code that uses continue>
<increment>
}

with
<declaration>;
while (<condition>) {
<code that uses continue>
<increment>
}

which do not differ significantly in the required lifetime of the
declared object.

I believe they do, because of 6.8.5 p5.
 
T

Tim Rentsch

Keith Thompson said:
Shao Miller said:
Sure thing. C99 6.8.5.3p1 with 6.2.4p6.

Personally, these days I like to declare everything at the top of a
function, so I don't actually take advantage of this, but it might be
worth noting, for C >= C99.

6.8.5.3p1:
If *clause-1* is a declaration, the scope of any identifiers
it declares is the remainder of the declaration and the entire
loop, including the other two expressions

6.2.4p6:
For such an object that does not have a variable length array type,
its lifetime extends from entry into the block with which it is
associated until execution of that block ends in any way.

So my interpretation of that is:

void foo(void) { /* lifetime of i starts here */
this();
that();
for (int i = 0 /* scope of i starts here */; i < 100; i ++) {
the_other_thing(i);
/* scope of i ends here */
}
yet_another_thing();
/* lifetime of i ends here */
}

The "block with which it is associated" is the outer block for the
function, not the block that is the statement controlled by the for
loop.


Actually it's the block of the iteration ('for') statement.
Remember that the statement controlled by a for loop needn't be a block:

for (int i = 0; i < 100; i ++)
single_statement();

The loop body of an iteration statement is always a block, even if
just a single statement (ie, not a compound statement).
If `i` were defined just above the for loop, the lifetime and scope would
be the same as they are in my example.

Declaring in the 'for' statement, as opposed to just before, is
more limited in both scope and lifetime. All of these follow from
6.8.5 p5.
 
B

Ben Bacarisse

Tim Rentsch said:
I believe they do, because of 6.8.5 p5.

Ah! Thank you. This not only makes things very clear (in particular my
fussing over footnote 27 is irrelevant since it clearly applies when a
for loop exits) it explains what looked to me like inconsistent wording
between 6.2.1 (Scope of identifiers) p4 and 5.8.5.3 (The for statement)
p1.

I think it could be better placed, but at least it's there.
 
K

Keith Thompson

Tim Rentsch said:
6.8.5.3p1:
If *clause-1* is a declaration, the scope of any identifiers
it declares is the remainder of the declaration and the entire
loop, including the other two expressions

6.2.4p6:
For such an object that does not have a variable length array type,
its lifetime extends from entry into the block with which it is
associated until execution of that block ends in any way.

So my interpretation of that is:

void foo(void) { /* lifetime of i starts here */
this();
that();
for (int i = 0 /* scope of i starts here */; i < 100; i ++) {
the_other_thing(i);
/* scope of i ends here */
}
yet_another_thing();
/* lifetime of i ends here */
}

The "block with which it is associated" is the outer block for the
function, not the block that is the statement controlled by the for
loop.


Actually it's the block of the iteration ('for') statement.
Remember that the statement controlled by a for loop needn't be a block:

for (int i = 0; i < 100; i ++)
single_statement();

The loop body of an iteration statement is always a block, even if
just a single statement (ie, not a compound statement).


You're right. I've been assuming that "block" and "compound-statement"
are synonymous, or nearly so, but N1570 6.8.5p5 says:

An iteration statement is a block whose scope is a strict
subset of the scope of its enclosing block. The loop body is
also a block whose scope is a strict subset of the scope of
the iteration statement.

(It would have been nice if that had been mentioned in the definition
of the word "block" in 6.8p3, but that so-called "definition"
describes how blocks behave, not what they are.)
Declaring in the 'for' statement, as opposed to just before, is
more limited in both scope and lifetime. All of these follow from
6.8.5 p5.

Yup.
 
K

Keith Thompson

pete said:
Keith Thompson wrote: [...]
The "block with which it is associated" is the outer block for the
function, not the block that is the statement controlled by the for
loop.


No.


You're right.
If you're going to say that there is one block
with which (i) is associated,
then it is the iteration statement itself,
which is the block in which (i) is declared.

N1570
6.8.5 Iteration statements
5 An iteration statement is a block ...

In C90 "block" was synonymous with "compound statement".
In C99, that was changed to the way that C11 has it now.

Yes, I missed that.
 
P

Phil Carmody

Phil Carmody said:
This reinforces something that I've spotted historically - there seems to
be some strange fetish for using the swiss-army for(;;) where while() is
perfectly suitable.

Perhaps I should have worded those as just "for" and "while", I wasn't
trying to imply the "(;;)" part, just the iteration statements.
 
B

Ben Bacarisse

pete said:
for (;;) is special.

while () , doesn't compile,
even though K&R 3.5 says that they are equivalent.

That's a very odd reading of the text! I've tried a few times and I
can't get that meaning from what's written in my K&R.
 
B

Ben Bacarisse

pete said:
The for statement

for (expr1; expr2; expr3)
statement

is equivalent to

expr1;
while (expr2) {
statement
expr3;
}

except for the behavior of continue, which is described in 3.7.

Does it not go on to explain what happens when any/all of expr[123] are
missing? Mine does.

Even if you stopped there and chose to ignore the sentences that follow,
I don't see how you can conclude that the equivalence applies to "for
(;;)". You'd have to be sure that expr[123] can be "empty" and that's
not at all clear. We've been told about expressions in chapter 2, but
none of the have been "empty". We are told, right away, that it's
possible to omit these parts of the for loop, but the special meaning is
explained then and there in the sentences that explain that they can be
omitted.
 
J

James Kuyper

The for statement

for (expr1; expr2; expr3)
statement

is equivalent to

expr1;
while (expr2) {
statement
expr3;
}

except for the behavior of continue, which is described in 3.7.

There's a few other differences, the relevant one in this context being
that expr2 is optional if a for() statement, but mandatory in a while()
(6.8.5p1).
Also, what you've called expr1 is called clause-1 in the standard, to
reflect the fact that it can be either an expression or a declaration.
If it is a declaration, any identifiers it declares have a scope that
includes the entire for() statement, but does not extend to the
enclosing block, so you should enclose your 'while' replacement in {},
to give those identifiers the right scope.
 
B

Ben Bacarisse

pete said:
Ben said:
pete said:
Ben Bacarisse wrote:


Phil Carmody wrote:
This reinforces something that I've spotted historically
- there seems to be some strange fetish
for using the swiss-army for(;;)
where while() is perfectly suitable.

Perhaps I should have worded those as just
"for" and "while", I wasn't
trying to imply the "(;;)" part, just the iteration statements.

for (;;) is special.

while () , doesn't compile,
even though K&R 3.5 says that they are equivalent.

That's a very odd reading of the text! I've tried a few times and I
can't get that meaning from what's written in my K&R.

The for statement

for (expr1; expr2; expr3)
statement

is equivalent to

expr1;
while (expr2) {
statement
expr3;
}

except for the behavior of continue, which is described in 3.7.

Does it not go on to explain what happens when any/all of expr[123] are
missing? Mine does.

Even if you stopped there and chose to ignore the sentences that follow,
I don't see how you can conclude that the equivalence applies to "for
(;;)". You'd have to be sure that expr[123] can be "empty" and that's
not at all clear. We've been told about expressions in chapter 2, but
none of the have been "empty". We are told, right away, that it's
possible to omit these parts of the for loop, but the special meaning is
explained then and there in the sentences that explain that they can be
omitted.

If 3.5 had said that Superman and his sister
were equivalent except that Superman was a male, as described in 3.7.
and then the text said that Superman had x-ray vision,
I would take that to mean that Supergirl had x-ray vision too.

Which is not analogous to what K&R wrote. It does not say that all
forms of "for" can be mapped to the given "while". It says that some
forms (the ones that have three nonempty expressions) can be so mapped.
It then confirms that this is not a universal rule by detailing special
cases and how they affect the correspondence.
 
K

Keith Thompson

pete said:
Ben said:
pete said:
Ben Bacarisse wrote:
for (;;) is special.

while () , doesn't compile,
even though K&R 3.5 says that they are equivalent.

That's a very odd reading of the text! I've tried a few times and I
can't get that meaning from what's written in my K&R.

The for statement

for (expr1; expr2; expr3)
statement

is equivalent to

expr1;
while (expr2) {
statement
expr3;
}

except for the behavior of continue, which is described in 3.7.

Does it not go on to explain what happens when any/all of expr[123] are
missing? Mine does.

Even if you stopped there and chose to ignore the sentences that follow,
I don't see how you can conclude that the equivalence applies to "for
(;;)". You'd have to be sure that expr[123] can be "empty" and that's
not at all clear. We've been told about expressions in chapter 2, but
none of the have been "empty". We are told, right away, that it's
possible to omit these parts of the for loop, but the special meaning is
explained then and there in the sentences that explain that they can be
omitted.

If 3.5 had said that Superman and his sister
were equivalent except that Superman was a male, as described in 3.7.
and then the text said that Superman had x-ray vision,
I would take that to mean that Supergirl had x-ray vision too.

The text doesn't say that for loops *in general* are equivalent to
the given while loop. It says that

for (expr1; expr2; expr3)
statement

is equivalent to the given while loop. It refers to a *subset*
of all for loops, i.e., those for which all three exprs are present.

It goes on to say that any or all of expr1, expr2, and expr3 may
be *omitted*, not that any of them may be *empty*. (And the names
imply that they're expressions; there's no such thing as an empty
expression.) If expr1 or expr3 is omitted, it's simply dropped
from the expansion. If expr2 is omitted, K&R doesn't even show an
equivalent while loop; it merely says that the for loop is infinite.
 
P

Phil Carmody

pete said:
for (;;) is special.

while () , doesn't compile,
even though K&R 3.5 says that they are equivalent.

Now read what I just wrote, and pay attention to where I say you should ignore the
open bracket, the two semicolons, and the closed bracket.

Phil
 

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
474,077
Messages
2,570,567
Members
47,204
Latest member
abhinav72673

Latest Threads

Top