Declare structure within for?
Apple LLVM 9.1.0 with clang-902.0.39.2, using -std=c11
, accepts:
typedef struct { int i; float f; } S;
for (S s = { 0, 0 }; s.i < 25; ++s.i, s.f = i/10.f)
…
but rejects:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
with the message:
error: declaration of non-local variable in 'for' loop
Is Clang correct to reject this, because it violates some constraint in the C standard? Which clause and paragraph? Or is it a Clang bug?
c for-loop struct clang language-lawyer
add a comment |
Apple LLVM 9.1.0 with clang-902.0.39.2, using -std=c11
, accepts:
typedef struct { int i; float f; } S;
for (S s = { 0, 0 }; s.i < 25; ++s.i, s.f = i/10.f)
…
but rejects:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
with the message:
error: declaration of non-local variable in 'for' loop
Is Clang correct to reject this, because it violates some constraint in the C standard? Which clause and paragraph? Or is it a Clang bug?
c for-loop struct clang language-lawyer
I reopened this because I drafted a new answer exploring the situation with more analysis and because I think the accepted answer in the previous original is incorrect.
– Eric Postpischil
Dec 1 '18 at 1:08
add a comment |
Apple LLVM 9.1.0 with clang-902.0.39.2, using -std=c11
, accepts:
typedef struct { int i; float f; } S;
for (S s = { 0, 0 }; s.i < 25; ++s.i, s.f = i/10.f)
…
but rejects:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
with the message:
error: declaration of non-local variable in 'for' loop
Is Clang correct to reject this, because it violates some constraint in the C standard? Which clause and paragraph? Or is it a Clang bug?
c for-loop struct clang language-lawyer
Apple LLVM 9.1.0 with clang-902.0.39.2, using -std=c11
, accepts:
typedef struct { int i; float f; } S;
for (S s = { 0, 0 }; s.i < 25; ++s.i, s.f = i/10.f)
…
but rejects:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
with the message:
error: declaration of non-local variable in 'for' loop
Is Clang correct to reject this, because it violates some constraint in the C standard? Which clause and paragraph? Or is it a Clang bug?
c for-loop struct clang language-lawyer
c for-loop struct clang language-lawyer
edited Nov 25 '18 at 14:05
melpomene
61.7k54994
61.7k54994
asked Nov 25 '18 at 13:58
Eric PostpischilEric Postpischil
78.7k883166
78.7k883166
I reopened this because I drafted a new answer exploring the situation with more analysis and because I think the accepted answer in the previous original is incorrect.
– Eric Postpischil
Dec 1 '18 at 1:08
add a comment |
I reopened this because I drafted a new answer exploring the situation with more analysis and because I think the accepted answer in the previous original is incorrect.
– Eric Postpischil
Dec 1 '18 at 1:08
I reopened this because I drafted a new answer exploring the situation with more analysis and because I think the accepted answer in the previous original is incorrect.
– Eric Postpischil
Dec 1 '18 at 1:08
I reopened this because I drafted a new answer exploring the situation with more analysis and because I think the accepted answer in the previous original is incorrect.
– Eric Postpischil
Dec 1 '18 at 1:08
add a comment |
2 Answers
2
active
oldest
votes
C11:
6.8.5.3 The for statement
1 The statement
for ( clause-1 ; expression-2 ; expression-3 ) statement
behaves as follows: The expression expression-2 is the controlling expression that is
evaluated before each execution of the loop body. The expression expression-3 is
evaluated as a void expression after each execution of the loop body. 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; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.158)
Left out [2], just speaking of handling ommited clauses/expressions...
158) Thus, clause-1 specifies initialization for the loop, possibly declaring one or more variables for use in the loop; the controlling expression, expression-2, specifies an evaluation made before each iteration, such that execution of the loop continues until the expression compares equal to 0; and expression-3 specifies an operation (such as incrementing) that is performed after each iteration.
Nothing mentioned about any restrictions. So as second example is a valid declaration – and I assume clang does not complain about the following either:
void f(void)
{
struct { int x, y; } point;
// use point...
}
– then the second example should be valid C code, so as far as I see, clang is wrong in rejecting.
There may be a restriction somewhere else in the standard, and I suspect there is. Considerfor (static int i = 0;…)
. We know that is not what the declaration inside afor
is for, so it would make sense that somewhere else in the C standard there is a restriction that things declared inside afor
must have an automatic storage duration, for example. If we find that, we might see whether its phrasing also excludes declaring new structure types inside afor
(Clang is right) or not (Clang is wrong—developer misinterpreted the restriction?).
– Eric Postpischil
Nov 25 '18 at 14:22
@EricPostpischil Good point. But I looked over the subsequent chapters of the standard as well, although admittted, not too thoroughly, as they address totally different topics. Annex A.2.3.6.8.5 comes back to, but no further information either (it is informative only anyway). The index only references the already stated section.
– Aconcagua
Nov 25 '18 at 14:38
It almost looks as if static variables in for loop initializers actually were legal. Interesting: C++17 has similar wording, but a bit more extent. GCC rejects static initializer for C, but not for C++! Not sure which of both is correct according to the (respective?) standard...
– Aconcagua
Nov 25 '18 at 14:45
C11 §6.8.5 Iteration statements ¶3: The declaration part of afor
statement shall only declare identifiers for objects having storage classauto
orregister
. That is not in the section onfor
only, but in the generic 'iteration statements' section.
– Jonathan Leffler
Nov 25 '18 at 16:20
I'm not sure whether declaring the identifiers for the members of astruct
is meant to be included in 'only declare identifiers for objects'. Since the structure is tagless when declared in the loop, it is only the members that might run foul of that 'only' restriction, but it isn't 100% clear to me that a structure type is an object. A tagged structure might run foul of it — and distinguishing between tagged and tagless structures would probably not be intentional. A tagged structure type would only be visible within the loop (§6.8.5 ¶5). It feels to me like an ambiguity in the standard.
– Jonathan Leffler
Nov 25 '18 at 16:32
|
show 2 more comments
Summary
The potential violation of the C standard lies with this sentence in C 2018 6.8.5 3:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
Since struct { int i; float f; }
declares both a type and an identifier, there is some question about how to interpret 6.8.5 3. It appears to me that:
- It is likely the committee intended to prohibit declaring anything but
identifiers forauto
orregister
objects. - This use case where a type is incidentally declared may not have been considered.
- Allowing this incidental declaration would be harmless and not considerably out of line with the intent.
(I would invite anybody more familiar with C committee records to bring to our attention anything pertaining to this.)
(I give references to the 2018 C standard in this answer, but the language
is old and exists in previous versions, perhaps with some different numbering
of clauses or paragraphs.)
Both a Type and an Identifier Are Declared
The declaration in the following for
statement declares both an identifier s
and an unnamed type:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
We know it declares a type because C 2018 6.7.2.1 8 says:
The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type, within a translation unit.
Per 6.7.2.1 1, struct { int i; float f; }
is a struct-or-union-specifier, and, within it, int i; float f;
is a struct-declaration list. So this source code matches the description of 6.7.2.1 8, so it declares a type.
The Language of the C Standard is Ambiguous
C 2018 6.8.5 3 says:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
As a matter of English grammar and use, several meanings are possible for this sentence, including:
- The only things the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers for objects the declaration shall declare are identifiers for objects having storage class
auto
orregister
.
Primarily, the problem is the “only” is not adjacent to the thing it is modifying. The “only” could be modifying “identifiers” or “objects” or “storage class.” One might prefer the modifier to modify the candidate nearest it, but the authors of sentences do not always construct them thusly. (Grammatically, it could also modify “having,” thus qualifying the objects as having only storage class auto
or register
and not having anything else, such as not having size or other properties. We easily rule out this meaning on semantic rather than grammatical grounds.)
These samples illustrate differences between the meanings:
static int s // Prohibited by 1, 2, and 3.
extern int s(int) // Prohibited by 1 and 2, permitted by 3.
struct { int i; float f; } s // Prohibited by 1, permitted by 2 and 3.
int s // Permitted by 1, 2, and 3.
Effects May Illuminate Intent
It does not appear there is a reason for preferring any of these meanings based on difficulties of implementing C. To see this, consider that a C implementation may easily rewrite:
for (declaration; …; …) …
to the equivalent code:
{ declaration; for (; …; …) … }
Thus, if a C implementation can support declarations and for
statements in general, it can support general declarations in a for
statement without significant additional effort.
What then is the purpose of 6.8.5 3?
The declaration in a for
statement provides convenience. It provides a nice way of declaring some iterator or other object used to control the loop, while limiting the scope to the for
statement (which is a benefit for avoiding bugs). It does not provide any new function. Given this, I expect 6.8.5 3 was written with the intent of enabling the declaration to serve this purpose without opening it up to other purposes. It would be odd, although not impossible, to use either of the first two sample declarations above in a for
statement.
If so, I suspect the surface intent of the committee was meaning 1 but that they did not consider the situation where an unnamed type is incidentally declared. When we reflect on the third sample, using a structure, we see that it is unusual but is not too out of line with the customary use of a for
statement:
- It arises naturally as a solution to the problem that only one declaration may be present in the declaration part of a
for
statement, yet sometimes it is useful to manage the loop with multiple objects of different types. - It is still an object with automatic storage generation, as intended for
for
loops. - The type that is technically declared is not needed outside the
for
loop.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53468223%2fdeclare-structure-within-for%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
C11:
6.8.5.3 The for statement
1 The statement
for ( clause-1 ; expression-2 ; expression-3 ) statement
behaves as follows: The expression expression-2 is the controlling expression that is
evaluated before each execution of the loop body. The expression expression-3 is
evaluated as a void expression after each execution of the loop body. 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; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.158)
Left out [2], just speaking of handling ommited clauses/expressions...
158) Thus, clause-1 specifies initialization for the loop, possibly declaring one or more variables for use in the loop; the controlling expression, expression-2, specifies an evaluation made before each iteration, such that execution of the loop continues until the expression compares equal to 0; and expression-3 specifies an operation (such as incrementing) that is performed after each iteration.
Nothing mentioned about any restrictions. So as second example is a valid declaration – and I assume clang does not complain about the following either:
void f(void)
{
struct { int x, y; } point;
// use point...
}
– then the second example should be valid C code, so as far as I see, clang is wrong in rejecting.
There may be a restriction somewhere else in the standard, and I suspect there is. Considerfor (static int i = 0;…)
. We know that is not what the declaration inside afor
is for, so it would make sense that somewhere else in the C standard there is a restriction that things declared inside afor
must have an automatic storage duration, for example. If we find that, we might see whether its phrasing also excludes declaring new structure types inside afor
(Clang is right) or not (Clang is wrong—developer misinterpreted the restriction?).
– Eric Postpischil
Nov 25 '18 at 14:22
@EricPostpischil Good point. But I looked over the subsequent chapters of the standard as well, although admittted, not too thoroughly, as they address totally different topics. Annex A.2.3.6.8.5 comes back to, but no further information either (it is informative only anyway). The index only references the already stated section.
– Aconcagua
Nov 25 '18 at 14:38
It almost looks as if static variables in for loop initializers actually were legal. Interesting: C++17 has similar wording, but a bit more extent. GCC rejects static initializer for C, but not for C++! Not sure which of both is correct according to the (respective?) standard...
– Aconcagua
Nov 25 '18 at 14:45
C11 §6.8.5 Iteration statements ¶3: The declaration part of afor
statement shall only declare identifiers for objects having storage classauto
orregister
. That is not in the section onfor
only, but in the generic 'iteration statements' section.
– Jonathan Leffler
Nov 25 '18 at 16:20
I'm not sure whether declaring the identifiers for the members of astruct
is meant to be included in 'only declare identifiers for objects'. Since the structure is tagless when declared in the loop, it is only the members that might run foul of that 'only' restriction, but it isn't 100% clear to me that a structure type is an object. A tagged structure might run foul of it — and distinguishing between tagged and tagless structures would probably not be intentional. A tagged structure type would only be visible within the loop (§6.8.5 ¶5). It feels to me like an ambiguity in the standard.
– Jonathan Leffler
Nov 25 '18 at 16:32
|
show 2 more comments
C11:
6.8.5.3 The for statement
1 The statement
for ( clause-1 ; expression-2 ; expression-3 ) statement
behaves as follows: The expression expression-2 is the controlling expression that is
evaluated before each execution of the loop body. The expression expression-3 is
evaluated as a void expression after each execution of the loop body. 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; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.158)
Left out [2], just speaking of handling ommited clauses/expressions...
158) Thus, clause-1 specifies initialization for the loop, possibly declaring one or more variables for use in the loop; the controlling expression, expression-2, specifies an evaluation made before each iteration, such that execution of the loop continues until the expression compares equal to 0; and expression-3 specifies an operation (such as incrementing) that is performed after each iteration.
Nothing mentioned about any restrictions. So as second example is a valid declaration – and I assume clang does not complain about the following either:
void f(void)
{
struct { int x, y; } point;
// use point...
}
– then the second example should be valid C code, so as far as I see, clang is wrong in rejecting.
There may be a restriction somewhere else in the standard, and I suspect there is. Considerfor (static int i = 0;…)
. We know that is not what the declaration inside afor
is for, so it would make sense that somewhere else in the C standard there is a restriction that things declared inside afor
must have an automatic storage duration, for example. If we find that, we might see whether its phrasing also excludes declaring new structure types inside afor
(Clang is right) or not (Clang is wrong—developer misinterpreted the restriction?).
– Eric Postpischil
Nov 25 '18 at 14:22
@EricPostpischil Good point. But I looked over the subsequent chapters of the standard as well, although admittted, not too thoroughly, as they address totally different topics. Annex A.2.3.6.8.5 comes back to, but no further information either (it is informative only anyway). The index only references the already stated section.
– Aconcagua
Nov 25 '18 at 14:38
It almost looks as if static variables in for loop initializers actually were legal. Interesting: C++17 has similar wording, but a bit more extent. GCC rejects static initializer for C, but not for C++! Not sure which of both is correct according to the (respective?) standard...
– Aconcagua
Nov 25 '18 at 14:45
C11 §6.8.5 Iteration statements ¶3: The declaration part of afor
statement shall only declare identifiers for objects having storage classauto
orregister
. That is not in the section onfor
only, but in the generic 'iteration statements' section.
– Jonathan Leffler
Nov 25 '18 at 16:20
I'm not sure whether declaring the identifiers for the members of astruct
is meant to be included in 'only declare identifiers for objects'. Since the structure is tagless when declared in the loop, it is only the members that might run foul of that 'only' restriction, but it isn't 100% clear to me that a structure type is an object. A tagged structure might run foul of it — and distinguishing between tagged and tagless structures would probably not be intentional. A tagged structure type would only be visible within the loop (§6.8.5 ¶5). It feels to me like an ambiguity in the standard.
– Jonathan Leffler
Nov 25 '18 at 16:32
|
show 2 more comments
C11:
6.8.5.3 The for statement
1 The statement
for ( clause-1 ; expression-2 ; expression-3 ) statement
behaves as follows: The expression expression-2 is the controlling expression that is
evaluated before each execution of the loop body. The expression expression-3 is
evaluated as a void expression after each execution of the loop body. 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; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.158)
Left out [2], just speaking of handling ommited clauses/expressions...
158) Thus, clause-1 specifies initialization for the loop, possibly declaring one or more variables for use in the loop; the controlling expression, expression-2, specifies an evaluation made before each iteration, such that execution of the loop continues until the expression compares equal to 0; and expression-3 specifies an operation (such as incrementing) that is performed after each iteration.
Nothing mentioned about any restrictions. So as second example is a valid declaration – and I assume clang does not complain about the following either:
void f(void)
{
struct { int x, y; } point;
// use point...
}
– then the second example should be valid C code, so as far as I see, clang is wrong in rejecting.
C11:
6.8.5.3 The for statement
1 The statement
for ( clause-1 ; expression-2 ; expression-3 ) statement
behaves as follows: The expression expression-2 is the controlling expression that is
evaluated before each execution of the loop body. The expression expression-3 is
evaluated as a void expression after each execution of the loop body. 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; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.158)
Left out [2], just speaking of handling ommited clauses/expressions...
158) Thus, clause-1 specifies initialization for the loop, possibly declaring one or more variables for use in the loop; the controlling expression, expression-2, specifies an evaluation made before each iteration, such that execution of the loop continues until the expression compares equal to 0; and expression-3 specifies an operation (such as incrementing) that is performed after each iteration.
Nothing mentioned about any restrictions. So as second example is a valid declaration – and I assume clang does not complain about the following either:
void f(void)
{
struct { int x, y; } point;
// use point...
}
– then the second example should be valid C code, so as far as I see, clang is wrong in rejecting.
answered Nov 25 '18 at 14:12
AconcaguaAconcagua
12.8k32144
12.8k32144
There may be a restriction somewhere else in the standard, and I suspect there is. Considerfor (static int i = 0;…)
. We know that is not what the declaration inside afor
is for, so it would make sense that somewhere else in the C standard there is a restriction that things declared inside afor
must have an automatic storage duration, for example. If we find that, we might see whether its phrasing also excludes declaring new structure types inside afor
(Clang is right) or not (Clang is wrong—developer misinterpreted the restriction?).
– Eric Postpischil
Nov 25 '18 at 14:22
@EricPostpischil Good point. But I looked over the subsequent chapters of the standard as well, although admittted, not too thoroughly, as they address totally different topics. Annex A.2.3.6.8.5 comes back to, but no further information either (it is informative only anyway). The index only references the already stated section.
– Aconcagua
Nov 25 '18 at 14:38
It almost looks as if static variables in for loop initializers actually were legal. Interesting: C++17 has similar wording, but a bit more extent. GCC rejects static initializer for C, but not for C++! Not sure which of both is correct according to the (respective?) standard...
– Aconcagua
Nov 25 '18 at 14:45
C11 §6.8.5 Iteration statements ¶3: The declaration part of afor
statement shall only declare identifiers for objects having storage classauto
orregister
. That is not in the section onfor
only, but in the generic 'iteration statements' section.
– Jonathan Leffler
Nov 25 '18 at 16:20
I'm not sure whether declaring the identifiers for the members of astruct
is meant to be included in 'only declare identifiers for objects'. Since the structure is tagless when declared in the loop, it is only the members that might run foul of that 'only' restriction, but it isn't 100% clear to me that a structure type is an object. A tagged structure might run foul of it — and distinguishing between tagged and tagless structures would probably not be intentional. A tagged structure type would only be visible within the loop (§6.8.5 ¶5). It feels to me like an ambiguity in the standard.
– Jonathan Leffler
Nov 25 '18 at 16:32
|
show 2 more comments
There may be a restriction somewhere else in the standard, and I suspect there is. Considerfor (static int i = 0;…)
. We know that is not what the declaration inside afor
is for, so it would make sense that somewhere else in the C standard there is a restriction that things declared inside afor
must have an automatic storage duration, for example. If we find that, we might see whether its phrasing also excludes declaring new structure types inside afor
(Clang is right) or not (Clang is wrong—developer misinterpreted the restriction?).
– Eric Postpischil
Nov 25 '18 at 14:22
@EricPostpischil Good point. But I looked over the subsequent chapters of the standard as well, although admittted, not too thoroughly, as they address totally different topics. Annex A.2.3.6.8.5 comes back to, but no further information either (it is informative only anyway). The index only references the already stated section.
– Aconcagua
Nov 25 '18 at 14:38
It almost looks as if static variables in for loop initializers actually were legal. Interesting: C++17 has similar wording, but a bit more extent. GCC rejects static initializer for C, but not for C++! Not sure which of both is correct according to the (respective?) standard...
– Aconcagua
Nov 25 '18 at 14:45
C11 §6.8.5 Iteration statements ¶3: The declaration part of afor
statement shall only declare identifiers for objects having storage classauto
orregister
. That is not in the section onfor
only, but in the generic 'iteration statements' section.
– Jonathan Leffler
Nov 25 '18 at 16:20
I'm not sure whether declaring the identifiers for the members of astruct
is meant to be included in 'only declare identifiers for objects'. Since the structure is tagless when declared in the loop, it is only the members that might run foul of that 'only' restriction, but it isn't 100% clear to me that a structure type is an object. A tagged structure might run foul of it — and distinguishing between tagged and tagless structures would probably not be intentional. A tagged structure type would only be visible within the loop (§6.8.5 ¶5). It feels to me like an ambiguity in the standard.
– Jonathan Leffler
Nov 25 '18 at 16:32
There may be a restriction somewhere else in the standard, and I suspect there is. Consider
for (static int i = 0;…)
. We know that is not what the declaration inside a for
is for, so it would make sense that somewhere else in the C standard there is a restriction that things declared inside a for
must have an automatic storage duration, for example. If we find that, we might see whether its phrasing also excludes declaring new structure types inside a for
(Clang is right) or not (Clang is wrong—developer misinterpreted the restriction?).– Eric Postpischil
Nov 25 '18 at 14:22
There may be a restriction somewhere else in the standard, and I suspect there is. Consider
for (static int i = 0;…)
. We know that is not what the declaration inside a for
is for, so it would make sense that somewhere else in the C standard there is a restriction that things declared inside a for
must have an automatic storage duration, for example. If we find that, we might see whether its phrasing also excludes declaring new structure types inside a for
(Clang is right) or not (Clang is wrong—developer misinterpreted the restriction?).– Eric Postpischil
Nov 25 '18 at 14:22
@EricPostpischil Good point. But I looked over the subsequent chapters of the standard as well, although admittted, not too thoroughly, as they address totally different topics. Annex A.2.3.6.8.5 comes back to, but no further information either (it is informative only anyway). The index only references the already stated section.
– Aconcagua
Nov 25 '18 at 14:38
@EricPostpischil Good point. But I looked over the subsequent chapters of the standard as well, although admittted, not too thoroughly, as they address totally different topics. Annex A.2.3.6.8.5 comes back to, but no further information either (it is informative only anyway). The index only references the already stated section.
– Aconcagua
Nov 25 '18 at 14:38
It almost looks as if static variables in for loop initializers actually were legal. Interesting: C++17 has similar wording, but a bit more extent. GCC rejects static initializer for C, but not for C++! Not sure which of both is correct according to the (respective?) standard...
– Aconcagua
Nov 25 '18 at 14:45
It almost looks as if static variables in for loop initializers actually were legal. Interesting: C++17 has similar wording, but a bit more extent. GCC rejects static initializer for C, but not for C++! Not sure which of both is correct according to the (respective?) standard...
– Aconcagua
Nov 25 '18 at 14:45
C11 §6.8.5 Iteration statements ¶3: The declaration part of a
for
statement shall only declare identifiers for objects having storage class auto
or register
. That is not in the section on for
only, but in the generic 'iteration statements' section.– Jonathan Leffler
Nov 25 '18 at 16:20
C11 §6.8.5 Iteration statements ¶3: The declaration part of a
for
statement shall only declare identifiers for objects having storage class auto
or register
. That is not in the section on for
only, but in the generic 'iteration statements' section.– Jonathan Leffler
Nov 25 '18 at 16:20
I'm not sure whether declaring the identifiers for the members of a
struct
is meant to be included in 'only declare identifiers for objects'. Since the structure is tagless when declared in the loop, it is only the members that might run foul of that 'only' restriction, but it isn't 100% clear to me that a structure type is an object. A tagged structure might run foul of it — and distinguishing between tagged and tagless structures would probably not be intentional. A tagged structure type would only be visible within the loop (§6.8.5 ¶5). It feels to me like an ambiguity in the standard.– Jonathan Leffler
Nov 25 '18 at 16:32
I'm not sure whether declaring the identifiers for the members of a
struct
is meant to be included in 'only declare identifiers for objects'. Since the structure is tagless when declared in the loop, it is only the members that might run foul of that 'only' restriction, but it isn't 100% clear to me that a structure type is an object. A tagged structure might run foul of it — and distinguishing between tagged and tagless structures would probably not be intentional. A tagged structure type would only be visible within the loop (§6.8.5 ¶5). It feels to me like an ambiguity in the standard.– Jonathan Leffler
Nov 25 '18 at 16:32
|
show 2 more comments
Summary
The potential violation of the C standard lies with this sentence in C 2018 6.8.5 3:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
Since struct { int i; float f; }
declares both a type and an identifier, there is some question about how to interpret 6.8.5 3. It appears to me that:
- It is likely the committee intended to prohibit declaring anything but
identifiers forauto
orregister
objects. - This use case where a type is incidentally declared may not have been considered.
- Allowing this incidental declaration would be harmless and not considerably out of line with the intent.
(I would invite anybody more familiar with C committee records to bring to our attention anything pertaining to this.)
(I give references to the 2018 C standard in this answer, but the language
is old and exists in previous versions, perhaps with some different numbering
of clauses or paragraphs.)
Both a Type and an Identifier Are Declared
The declaration in the following for
statement declares both an identifier s
and an unnamed type:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
We know it declares a type because C 2018 6.7.2.1 8 says:
The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type, within a translation unit.
Per 6.7.2.1 1, struct { int i; float f; }
is a struct-or-union-specifier, and, within it, int i; float f;
is a struct-declaration list. So this source code matches the description of 6.7.2.1 8, so it declares a type.
The Language of the C Standard is Ambiguous
C 2018 6.8.5 3 says:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
As a matter of English grammar and use, several meanings are possible for this sentence, including:
- The only things the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers for objects the declaration shall declare are identifiers for objects having storage class
auto
orregister
.
Primarily, the problem is the “only” is not adjacent to the thing it is modifying. The “only” could be modifying “identifiers” or “objects” or “storage class.” One might prefer the modifier to modify the candidate nearest it, but the authors of sentences do not always construct them thusly. (Grammatically, it could also modify “having,” thus qualifying the objects as having only storage class auto
or register
and not having anything else, such as not having size or other properties. We easily rule out this meaning on semantic rather than grammatical grounds.)
These samples illustrate differences between the meanings:
static int s // Prohibited by 1, 2, and 3.
extern int s(int) // Prohibited by 1 and 2, permitted by 3.
struct { int i; float f; } s // Prohibited by 1, permitted by 2 and 3.
int s // Permitted by 1, 2, and 3.
Effects May Illuminate Intent
It does not appear there is a reason for preferring any of these meanings based on difficulties of implementing C. To see this, consider that a C implementation may easily rewrite:
for (declaration; …; …) …
to the equivalent code:
{ declaration; for (; …; …) … }
Thus, if a C implementation can support declarations and for
statements in general, it can support general declarations in a for
statement without significant additional effort.
What then is the purpose of 6.8.5 3?
The declaration in a for
statement provides convenience. It provides a nice way of declaring some iterator or other object used to control the loop, while limiting the scope to the for
statement (which is a benefit for avoiding bugs). It does not provide any new function. Given this, I expect 6.8.5 3 was written with the intent of enabling the declaration to serve this purpose without opening it up to other purposes. It would be odd, although not impossible, to use either of the first two sample declarations above in a for
statement.
If so, I suspect the surface intent of the committee was meaning 1 but that they did not consider the situation where an unnamed type is incidentally declared. When we reflect on the third sample, using a structure, we see that it is unusual but is not too out of line with the customary use of a for
statement:
- It arises naturally as a solution to the problem that only one declaration may be present in the declaration part of a
for
statement, yet sometimes it is useful to manage the loop with multiple objects of different types. - It is still an object with automatic storage generation, as intended for
for
loops. - The type that is technically declared is not needed outside the
for
loop.
add a comment |
Summary
The potential violation of the C standard lies with this sentence in C 2018 6.8.5 3:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
Since struct { int i; float f; }
declares both a type and an identifier, there is some question about how to interpret 6.8.5 3. It appears to me that:
- It is likely the committee intended to prohibit declaring anything but
identifiers forauto
orregister
objects. - This use case where a type is incidentally declared may not have been considered.
- Allowing this incidental declaration would be harmless and not considerably out of line with the intent.
(I would invite anybody more familiar with C committee records to bring to our attention anything pertaining to this.)
(I give references to the 2018 C standard in this answer, but the language
is old and exists in previous versions, perhaps with some different numbering
of clauses or paragraphs.)
Both a Type and an Identifier Are Declared
The declaration in the following for
statement declares both an identifier s
and an unnamed type:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
We know it declares a type because C 2018 6.7.2.1 8 says:
The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type, within a translation unit.
Per 6.7.2.1 1, struct { int i; float f; }
is a struct-or-union-specifier, and, within it, int i; float f;
is a struct-declaration list. So this source code matches the description of 6.7.2.1 8, so it declares a type.
The Language of the C Standard is Ambiguous
C 2018 6.8.5 3 says:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
As a matter of English grammar and use, several meanings are possible for this sentence, including:
- The only things the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers for objects the declaration shall declare are identifiers for objects having storage class
auto
orregister
.
Primarily, the problem is the “only” is not adjacent to the thing it is modifying. The “only” could be modifying “identifiers” or “objects” or “storage class.” One might prefer the modifier to modify the candidate nearest it, but the authors of sentences do not always construct them thusly. (Grammatically, it could also modify “having,” thus qualifying the objects as having only storage class auto
or register
and not having anything else, such as not having size or other properties. We easily rule out this meaning on semantic rather than grammatical grounds.)
These samples illustrate differences between the meanings:
static int s // Prohibited by 1, 2, and 3.
extern int s(int) // Prohibited by 1 and 2, permitted by 3.
struct { int i; float f; } s // Prohibited by 1, permitted by 2 and 3.
int s // Permitted by 1, 2, and 3.
Effects May Illuminate Intent
It does not appear there is a reason for preferring any of these meanings based on difficulties of implementing C. To see this, consider that a C implementation may easily rewrite:
for (declaration; …; …) …
to the equivalent code:
{ declaration; for (; …; …) … }
Thus, if a C implementation can support declarations and for
statements in general, it can support general declarations in a for
statement without significant additional effort.
What then is the purpose of 6.8.5 3?
The declaration in a for
statement provides convenience. It provides a nice way of declaring some iterator or other object used to control the loop, while limiting the scope to the for
statement (which is a benefit for avoiding bugs). It does not provide any new function. Given this, I expect 6.8.5 3 was written with the intent of enabling the declaration to serve this purpose without opening it up to other purposes. It would be odd, although not impossible, to use either of the first two sample declarations above in a for
statement.
If so, I suspect the surface intent of the committee was meaning 1 but that they did not consider the situation where an unnamed type is incidentally declared. When we reflect on the third sample, using a structure, we see that it is unusual but is not too out of line with the customary use of a for
statement:
- It arises naturally as a solution to the problem that only one declaration may be present in the declaration part of a
for
statement, yet sometimes it is useful to manage the loop with multiple objects of different types. - It is still an object with automatic storage generation, as intended for
for
loops. - The type that is technically declared is not needed outside the
for
loop.
add a comment |
Summary
The potential violation of the C standard lies with this sentence in C 2018 6.8.5 3:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
Since struct { int i; float f; }
declares both a type and an identifier, there is some question about how to interpret 6.8.5 3. It appears to me that:
- It is likely the committee intended to prohibit declaring anything but
identifiers forauto
orregister
objects. - This use case where a type is incidentally declared may not have been considered.
- Allowing this incidental declaration would be harmless and not considerably out of line with the intent.
(I would invite anybody more familiar with C committee records to bring to our attention anything pertaining to this.)
(I give references to the 2018 C standard in this answer, but the language
is old and exists in previous versions, perhaps with some different numbering
of clauses or paragraphs.)
Both a Type and an Identifier Are Declared
The declaration in the following for
statement declares both an identifier s
and an unnamed type:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
We know it declares a type because C 2018 6.7.2.1 8 says:
The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type, within a translation unit.
Per 6.7.2.1 1, struct { int i; float f; }
is a struct-or-union-specifier, and, within it, int i; float f;
is a struct-declaration list. So this source code matches the description of 6.7.2.1 8, so it declares a type.
The Language of the C Standard is Ambiguous
C 2018 6.8.5 3 says:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
As a matter of English grammar and use, several meanings are possible for this sentence, including:
- The only things the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers for objects the declaration shall declare are identifiers for objects having storage class
auto
orregister
.
Primarily, the problem is the “only” is not adjacent to the thing it is modifying. The “only” could be modifying “identifiers” or “objects” or “storage class.” One might prefer the modifier to modify the candidate nearest it, but the authors of sentences do not always construct them thusly. (Grammatically, it could also modify “having,” thus qualifying the objects as having only storage class auto
or register
and not having anything else, such as not having size or other properties. We easily rule out this meaning on semantic rather than grammatical grounds.)
These samples illustrate differences between the meanings:
static int s // Prohibited by 1, 2, and 3.
extern int s(int) // Prohibited by 1 and 2, permitted by 3.
struct { int i; float f; } s // Prohibited by 1, permitted by 2 and 3.
int s // Permitted by 1, 2, and 3.
Effects May Illuminate Intent
It does not appear there is a reason for preferring any of these meanings based on difficulties of implementing C. To see this, consider that a C implementation may easily rewrite:
for (declaration; …; …) …
to the equivalent code:
{ declaration; for (; …; …) … }
Thus, if a C implementation can support declarations and for
statements in general, it can support general declarations in a for
statement without significant additional effort.
What then is the purpose of 6.8.5 3?
The declaration in a for
statement provides convenience. It provides a nice way of declaring some iterator or other object used to control the loop, while limiting the scope to the for
statement (which is a benefit for avoiding bugs). It does not provide any new function. Given this, I expect 6.8.5 3 was written with the intent of enabling the declaration to serve this purpose without opening it up to other purposes. It would be odd, although not impossible, to use either of the first two sample declarations above in a for
statement.
If so, I suspect the surface intent of the committee was meaning 1 but that they did not consider the situation where an unnamed type is incidentally declared. When we reflect on the third sample, using a structure, we see that it is unusual but is not too out of line with the customary use of a for
statement:
- It arises naturally as a solution to the problem that only one declaration may be present in the declaration part of a
for
statement, yet sometimes it is useful to manage the loop with multiple objects of different types. - It is still an object with automatic storage generation, as intended for
for
loops. - The type that is technically declared is not needed outside the
for
loop.
Summary
The potential violation of the C standard lies with this sentence in C 2018 6.8.5 3:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
Since struct { int i; float f; }
declares both a type and an identifier, there is some question about how to interpret 6.8.5 3. It appears to me that:
- It is likely the committee intended to prohibit declaring anything but
identifiers forauto
orregister
objects. - This use case where a type is incidentally declared may not have been considered.
- Allowing this incidental declaration would be harmless and not considerably out of line with the intent.
(I would invite anybody more familiar with C committee records to bring to our attention anything pertaining to this.)
(I give references to the 2018 C standard in this answer, but the language
is old and exists in previous versions, perhaps with some different numbering
of clauses or paragraphs.)
Both a Type and an Identifier Are Declared
The declaration in the following for
statement declares both an identifier s
and an unnamed type:
for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
…
We know it declares a type because C 2018 6.7.2.1 8 says:
The presence of a struct-declaration-list in a struct-or-union-specifier declares a new type, within a translation unit.
Per 6.7.2.1 1, struct { int i; float f; }
is a struct-or-union-specifier, and, within it, int i; float f;
is a struct-declaration list. So this source code matches the description of 6.7.2.1 8, so it declares a type.
The Language of the C Standard is Ambiguous
C 2018 6.8.5 3 says:
The declaration part of a
for
statement shall only declare identifiers for objects having storage classauto
orregister
.
As a matter of English grammar and use, several meanings are possible for this sentence, including:
- The only things the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers the declaration shall declare are identifiers for objects having storage class
auto
orregister
. - The only identifiers for objects the declaration shall declare are identifiers for objects having storage class
auto
orregister
.
Primarily, the problem is the “only” is not adjacent to the thing it is modifying. The “only” could be modifying “identifiers” or “objects” or “storage class.” One might prefer the modifier to modify the candidate nearest it, but the authors of sentences do not always construct them thusly. (Grammatically, it could also modify “having,” thus qualifying the objects as having only storage class auto
or register
and not having anything else, such as not having size or other properties. We easily rule out this meaning on semantic rather than grammatical grounds.)
These samples illustrate differences between the meanings:
static int s // Prohibited by 1, 2, and 3.
extern int s(int) // Prohibited by 1 and 2, permitted by 3.
struct { int i; float f; } s // Prohibited by 1, permitted by 2 and 3.
int s // Permitted by 1, 2, and 3.
Effects May Illuminate Intent
It does not appear there is a reason for preferring any of these meanings based on difficulties of implementing C. To see this, consider that a C implementation may easily rewrite:
for (declaration; …; …) …
to the equivalent code:
{ declaration; for (; …; …) … }
Thus, if a C implementation can support declarations and for
statements in general, it can support general declarations in a for
statement without significant additional effort.
What then is the purpose of 6.8.5 3?
The declaration in a for
statement provides convenience. It provides a nice way of declaring some iterator or other object used to control the loop, while limiting the scope to the for
statement (which is a benefit for avoiding bugs). It does not provide any new function. Given this, I expect 6.8.5 3 was written with the intent of enabling the declaration to serve this purpose without opening it up to other purposes. It would be odd, although not impossible, to use either of the first two sample declarations above in a for
statement.
If so, I suspect the surface intent of the committee was meaning 1 but that they did not consider the situation where an unnamed type is incidentally declared. When we reflect on the third sample, using a structure, we see that it is unusual but is not too out of line with the customary use of a for
statement:
- It arises naturally as a solution to the problem that only one declaration may be present in the declaration part of a
for
statement, yet sometimes it is useful to manage the loop with multiple objects of different types. - It is still an object with automatic storage generation, as intended for
for
loops. - The type that is technically declared is not needed outside the
for
loop.
answered Dec 1 '18 at 1:05
Eric PostpischilEric Postpischil
78.7k883166
78.7k883166
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53468223%2fdeclare-structure-within-for%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I reopened this because I drafted a new answer exploring the situation with more analysis and because I think the accepted answer in the previous original is incorrect.
– Eric Postpischil
Dec 1 '18 at 1:08