在for循环中声明匿名结构,clang无法编译

在for循环中声明匿名结构,clang无法编译,c,c99,C,C99,在带有-std=c99/gnu99的gcc中,在for循环中声明匿名结构的代码运行良好 for (struct {int foo; int bar;} i = {0}; i.foo < 10; i.foo++); 为什么这是一个错误?为什么它允许某些类型(例如“int”)而不允许其他类型(例如struct{intfoo;})?这似乎不一致。是clang无法正确实现c99,还是代码无效,c99和gcc恰好支持它 有人知道一种方法可以在一个由clang支持的for循环中声明多种类型的变量吗?

在带有-std=c99/gnu99的gcc中,在for循环中声明匿名结构的代码运行良好

for (struct {int foo; int bar;} i = {0}; i.foo < 10; i.foo++);
为什么这是一个错误?为什么它允许某些类型(例如“int”)而不允许其他类型(例如struct{intfoo;})?这似乎不一致。是clang无法正确实现c99,还是代码无效,c99和gcc恰好支持它

有人知道一种方法可以在一个由clang支持的for循环中声明多种类型的变量吗?(这对宏很有用。)

编辑:

既然有人问这为什么有用,我将粘贴一些示例代码:

#define TREE_EACH(head, node, field, iterator) for ( \
    /* initialize */ \
    struct { \
        node* cur; \
        node* stack[((head)->th_root == 0? 0: (head)->th_root->field.avl_height) + 1]; \
        uint32_t stack_size; \
    } iterator = {.cur = (head)->th_root, .stack_size = 0}; \
    /* while */ \
    iterator.cur != 0; \
    /* iterate */ \
    (iterator.stack_size += (iterator.cur->field.avl_right != 0) \
        ? (iterator.stack[iterator.stack_size] = avl_right, 1) \
        : 0), \
    (iterator.cur = (iterator.cur->field.avl_left == 0) \
        ? iterator.cur->field.avl_left \
        : (iterator.stack_size > 0? (iterator.stack_size--, iterator.stack[iterator.stack_size]): 0)) \
)

这是我编写的一个非常方便的宏,它在堆栈上以深度优先顺序迭代AVL树。因为在for循环中声明匿名结构是不允许的,尽管我必须降低宏的使用直观性。我无法将声明的源代码输出到树的其余部分,因为它使用可变长度数组。

C99中不允许这样做。§6.8.5规定:

3 for声明的声明部分仅应声明 具有存储类
auto
register
的对象的标识符

您所显示的声明声明了对象
i
之外的类型,并且类型不是对象的标识符。

Summary 可能违反C标准的是C 2018 6.8.5 3中的这句话:

for
语句的声明部分应仅声明具有存储类
auto
寄存器的对象的标识符

由于
struct{int i;float f;}
同时声明了一个类型和一个标识符,因此关于如何解释6.8.5 3存在一些问题。在我看来:

  • 委员会很可能打算禁止申报任何东西,但
    auto
    register
    对象的标识符
  • 偶然声明类型的这个用例可能没有被考虑
  • 允许这一附带声明将是无害的,并且不会大大违背其意图
(我将邀请任何更熟悉C委员会记录的人提请我们注意与此相关的任何信息。)

(我在回答中引用了2018 C标准,但语言 是旧版本,存在于以前的版本中,可能有一些不同的编号 (指条款或段落。)

类型和标识符都被声明 以下
for
语句中的声明声明了标识符
s
和未命名类型:

for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
    …
效果可以说明意图 <> P>不存在有理由基于C实现的困难而优先考虑这些含义,考虑到C实现可以很容易地重写:

for (declaration; …; …) …
对于等效代码:

{ declaration; for (; …; …) … }
因此,如果C实现通常可以支持声明和
for
语句,那么它就可以支持
for
语句中的一般声明,而无需进行大量额外的工作

那么6.8.5.3的目的是什么

for
语句中的声明提供了方便。它提供了一种很好的方法来声明用于控制循环的迭代器或其他对象,同时将范围限制为
for
语句(这是避免bug的一个好处)。它不提供任何新功能。有鉴于此,我认为6.8.5.3的编写意图是使《宣言》能够达到这一目的,而不向其他目的开放。在
for
语句中使用上述前两个示例声明中的任何一个都是奇怪的,尽管并非不可能

如果是这样,我怀疑委员会的表面意图是指1,但他们没有考虑未命名的类型被偶然宣布的情况。当我们使用结构思考第三个示例时,我们发现这是不寻常的,但与
for
语句的习惯用法并不太不一致:

  • 作为问题的解决方案,它自然而然地出现在
    for
    语句的声明部分中,但有时使用不同类型的多个对象管理循环是有用的
  • 它仍然是一个具有自动存储生成功能的对象,用于
    for
    循环
  • for
    循环之外不需要技术上声明的类型

我对前面的回答并不信服。它使用gcc(使用
-Wall-pedantic
)成功构建,而不是使用clang或visualstudio

微软已经承认了一个与VisualStudio极为相似的问题

6.8.5表示for init表达式中的标识符声明不能是
typedef
extern
static
(除
auto
register
之外的唯一存储类说明符)

C99中的存储类说明符
auto
是默认值,是隐式的。然后,结构类型和标识符
i
是自动的(在该代码块内具有作用域)。那么代码是否有效?我不明白6.8.5是如何禁止类型声明的


我建议gcc是正确的,这是clang和Visual Studio实现的一个缺陷。

作为一种解决方法,您可以为每个循环一次的结构成员添加外部for循环。它很难看,但至少从使用角度来看,它将是相同的

为什么需要一个结构作为for循环中的局部变量?它实现了什么目的?因为record VS 2008也不能编译它。Jay:它在“foreach”风格的宏中很有用,因为迭代器很复杂(需要许多不同的类型)并且匿名,所以不会污染名称空间。在for之上声明它们本质上会使声明/标识符进入宏未使用的范围
for (declaration; …; …) …
{ declaration; for (; …; …) … }