8

for ループで匿名構造体を宣言するコードは、-std=c99/gnu99 を使用して gcc で正常に機能しました

for (struct {int foo; int bar;} i = {0}; i.foo < 10; i.foo++);

ただし、代わりにclangに切り替えると、エラーが発生しました:

error: declaration of non-local variable in 'for' loop

なぜこれがエラーになるのですか?一部の型 (例: "int") を許可し、他の型 (例: struct {int foo;}) を許可しないのはなぜですか? これは矛盾しているようです。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 ループで匿名の構造体を宣言することは許可されていないため、マクロを直感的に使用しにくくする必要があります。可変長配列を使用しているため、ツリーの残りの部分に宣言をアウトソーシングすることはできませんでした。

4

5 に答える 5

6

私は以前の答えに敬意を表して納得していません。gcc ( with -Wall -pedantic) で正常にビルドされますが、clang や Visual Studio ではビルドできません。

Microsoft は、このMicrosoft Connect バグ アイテムで、Visual Studio に関する非常に類似した問題をバグとして認めています。

6.8.5 は、for-init-expressiontypedef内の識別子の宣言は,externまたは( andstatic以外の唯一のストレージ クラス指定子) であってはならないと言っています。autoregister

C99のストレージ クラス指定子autoはデフォルトであり、暗黙的です。構造体の型と識別子iは auto です (そのコード ブロック内にスコープがあります)。確かにコードは有効ですか?6.8.5 がどのように型の宣言を禁止しているのかわかりません。

gcc が正しいことをお勧めします。これは、clang と Visual Studio による実装のバグです。

于 2012-08-11T08:53:07.417 に答える
5

概要

C 標準の潜在的な違反は、C 2018 6.8.5 3 の次の文にあります。

ステートメントの宣言部分では、forストレージ クラスautoまたはを持つオブジェクトの識別子のみを宣言する必要がありregisterます。

は型と識別子の両方を宣言しているため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)
    …

C 2018 6.7.2.1 8 が次のように述べているため、型を宣言していることがわかります。

struct-or-union-specifier に struct-declaration-list が存在すると、翻訳単位内で新しい型が宣言されます。

6.7.2.1 によると、1struct { int i; float f; }は struct-or-union-specifier であり、その中にint i; float f;は struct-declaration リストがあります。したがって、このソース コードは 6.7.2.1 8 の記述と一致するため、型を宣言します。

C標準の言語はあいまいです

C 2018 6.8.5 3 言います:

ステートメントの宣言部分では、forストレージ クラスautoまたはを持つオブジェクトの識別子のみを宣言する必要がありregisterます。

英語の文法と用法の問題として、この文には次のようないくつかの意味が考えられます。

  1. 宣言で宣言する必要があるのは、ストレージ クラスautoまたはを持つオブジェクトの識別子だけregisterです。
  2. 宣言で宣言する唯一の識別子は、ストレージ クラスautoまたはを持つオブジェクトの識別子ですregister
  3. 宣言が宣言するオブジェクトの唯一の識別子は、ストレージ クラスautoまたはを持つオブジェクトの識別子ですregister

主な問題は、「唯一」が変更対象に隣接していないことです。「唯一」とは、「識別子」、「オブジェクト」、または「ストレージ クラス」を変更することです。最も近い候補を修飾する修飾子を好む人もいるかもしれませんが、文の作成者は常にそのように文を構成するとは限りません。(文法的には、「having」を修飾することもできます。したがって、オブジェクトはストレージ クラスのみを持ち、サイズautoregisterその他のプロパティを持たないなど、他のものを持たないと見なされます。この意味は、文法的な根拠ではなく意味論的に簡単に除外できます。)

これらのサンプルは、意味の違いを示しています。

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.

効果は意図を照らす可能性がある

C の実装の難しさに基づいて、これらの意味のいずれかを優先する理由があるようには見えません。

for (declaration; …; …) …

同等のコードに:

{ declaration; for (; …; …) … }

したがって、C 実装が一般的な宣言とforステートメントをサポートできる場合、それはステートメントで一般的な宣言をサポートすることができforます。

では、6.8.5 3 の目的は何ですか?

ステートメント内の宣言forは利便性を提供します。forこれは、スコープをステートメントに限定しながら、ループを制御するために使用されるイテレーターまたはその他のオブジェクトを宣言する優れた方法を提供します(これは、バグを回避するための利点です)。新しい機能はありません。このことを考えると、6.8.5 3 は、宣言を他の目的に開放することなく、この目的を果たすことができるようにする意図で書かれたと思います。for上記の最初の 2 つのサンプル宣言のいずれかをステートメントで使用するのは、不可能ではありませんが、奇妙です。

もしそうなら、委員会の表面的な意図は 1 を意味していたが、名前のない型が偶発的に宣言された状況を考慮していなかったのではないかと思います。for構造を使用して 3 番目のサンプルを検討すると、それは珍しいことですが、ステートメントの通常の使用法とあまり一致していないことがわかります。

  • ステートメントの宣言部分に 1 つの宣言しか存在しないという問題の解決策として自然に発生しforますが、異なる型の複数のオブジェクトでループを管理すると便利な場合があります。
  • forループ用に意図されているように、自動ストレージ生成を備えたオブジェクトです。
  • 技術的に宣言された型は、forループの外では必要ありません。
于 2012-08-10T14:23:12.070 に答える
3

C99 では許可されていません。§6.8.5 は言う:

3 for ステートメントの宣言部分では、ストレージ クラスautoまたはを持つオブジェクトの識別子のみを宣言する必要がありregisterます。

あなたが示した宣言は、 object に加えて型を宣言していますがi、型はオブジェクトの識別子ではありません。

于 2012-08-10T14:22:06.783 に答える