2013-04-28 7 views
8

私は最近複雑な宣言を解読するためにthe spiral ruleを学びました。これは一連のtypedefで書かれていなければなりません。ただし、以下のコメントが私を警告する:私はvoid (*signal(int, void (*fp)(int)))(int);「単純なケース」を見つけていない宣言に関する渦巻きのルール - それは間違っていますか?

A frequently cited simplification, which only works for a few simple cases.

。ところで、それはもっと驚くべきことです。

私の質問は、です。その場合、ルールを適用するのに間違いがあり、エラーになることはありますか?

+3

私はあなたに 'Vorac'違う;私はそれが間違っている状況を認識していません。 –

+0

@Jonathan Leffler、既知の問題はなく、スパイラルルールは決して間違いありませんか? – Vorac

+1

OK - だから[James Kanze](http://stackoverflow.com/users/649665/james-kanze)who [views](http://stackoverflow.com/questions/7526152/easy-rule-to 1994年に投稿された記事(あなたではなく、誤った属性のためにお詫び申し上げます)には、-read-complex-const-declarations/7526298#comment23257858_7526298)私はジェームズの反対が何であるか分かりません。私はそれが失敗する状況を知りません(しかし、私はそれを勉強していません)。そのような状況はないと言っているのと同じではありませんが、私はそのような説明できない、綿密なコメントは非常に煙であると考えています。 –

答えて

2

例えば:

int * a[][5]; 

これはintの配列へのポインタのアレイではありません。

+0

これは何ですか? – yapkm01

+0

@ yapkm01 int型の5つのポインタからなる配列の配列です。 –

3

ルールは正しいです。しかし、それを適用する際に非常に慎重でなければならない。

私はC99 +宣言のためのより正式な方法でそれを適用することを提案します。

ここで最も重要なことは、すべての宣言(constvolatilestaticexterninlinestructuniontypedefを簡単にするために画像から削除されますが、簡単に戻って追加することができます)の以下の再帰構造を認識することです。

base-type [derived-part1: *'s] [object] [derived-part2: []'s or()] 

これは4つの部分です。

where 

    base-type is one of the following (I'm using a bit compressed notation): 
    void 
    [signed/unsigned] char 
    [signed/unsigned] short [int] 
    signed/unsigned [int] 
    [signed/unsigned] long [long] [int] 
    float 
    [long] double 
    etc 

    object is 
     an identifier 
    OR 
     ([derived-part1: *'s] [object] [derived-part2: []'s or()]) 

    * is *, denotes a reference/pointer and can be repeated 
    [] in derived-part2 denotes bracketed array dimensions and can be repeated 
() in derived-part2 denotes parenthesized function parameters delimited with ,'s 
    [] elsewhere denotes an optional part 
() elsewhere denotes parentheses 

あなたが解析されたすべての4つの部分を持ってたら、

    [object] [derived-part2(含む/戻る)] [derived-part2(ポインタへ)] base-type です。

再帰がある場合は、再帰スタックの一番下にobject(ある場合)があります。これは最も内側のものです。バックアップして収集して結合することで完全な宣言を得られます再帰の各レベルで導出された部分。

を解析すると、[derived-part2](該当する場合)の後に移動することがあります。これにより、線形でわかりやすい宣言が得られます(を参照)。

したがって、

char* (**(*foo[3][5])(void))[7][9]; 

にあなたが得る:

  1. base-type = char
  2. レベル1:= *derived-part1、= (**(*foo[3][5])(void))objectderived-part2 = [7][9]
  3. レベル2:derived-part1 = **、= (*foo[3][5])objectderived-part2 = (void)
  4. レベル3:= *derived-part1、= fooobjectderived-part2 = [3][5]

そこから:

  1. レベル3:*[3][5]foo
  2. レベル2:**(void)*[3][5]foo
  3. レベル1:今すぐ*[7][9]**(void)*[3][5]foo
  4. 最後に、char*[7][9]**(void)*[3][5]foo

、右から左へ読み:

fooは、charへの9つのポインタからなる7つの配列の配列へのポインタへのポインタを返す、関数への5つのポインタからなる3つの配列の配列です(パラメータなし)。

また、処理中のderived-part2に配列のサイズを逆にすることもできます。

これはあなたのスパイラルルールです。

そして、らせんを見るのは簡単です。あなたはより深くネストされた[object]を左からダイビングしてから、右にリサーフェイスするだけで、上のレベルに左右の別のペアがあることに注意してください。

+0

興味深い。まずルールが正しいと言います。そして、それを使用しない宣言をどのように読むのかを説明し、実際にはそれと矛盾します。 –

+0

あなたが再帰するとき(私は巣という言葉を使うだろうが、それは同じものになる)ということを言及するかもしれないが、宣言のネストされた部分は 'base-part'を含んでいない別の宣言にネストされた完全な宣言を持つ)。 –

+0

@JamesKanzeあなたが言いたいことはすぐに明らかになるはずです。 「オブジェクトは...」の下に「ベース型」がないことを確認してください。 –

16

基本的に言えば、ルールは単純例えば、仕事、またはそうでなければ 作品を、その場合には、 はそれにはポイントがありません(スパイラルが何を意味するのかを再定義することにより、考慮していない:。

int* a[10][15]; 

スパイラルルールは、intの 配列[15]へのポインタの配列[10]が間違っています。サイトの場合は、 も動作しません;実際にはsignalの場合それは ではなく、あなたが螺旋を始めるべき場所を明確にしていません。

一般的に、ルールが失敗する場所の例を見つけるのは、 よりも簡単です。

私はしばしば、C++宣言の解析は ですが、複雑な宣言を試みた人物は と信じていません。一方で、それは時々作られているようにそれほど難しくありません。 。秘密は、表現式と同じように 宣言を正確に考えることですが、演算子が少なく、非常に単純な優先規則があります。すべての演算子は、すべての演算子よりも優先されます。 に括弧がない場合は、 のすべてを最初に処理し、次にすべてを処理し、 というかっこを他の式とまったく同じように処理することを意味します。 実際の難易度がない構文そのものですが、それ 結果はいくつかの非常に複雑で、直感に反する宣言、 機能への関数の戻り値とポインタが含まれている、特に であること:最初の右、そして左のルールが を意味します特定のレベルでの演算子は、多くの場合、広く分離されていることを、 例えば:

int (*f(/* lots of parameters */))[10]; 

拡張の最後の項は、ここint[10]ですが、で(完全な機能仕様の後[10]を をされて置きます私には少なくとも)非常に不自然で、私はそれをやめて、毎回 を働かなければなりません。 ( 部分が論理的に隣り合って渦巻き規則につながるという傾向はおそらくありません。 の問題は、かっこがないと、いつでもが広がっていません。[i][j] 。ルールは)むしろスパイラルよりも、再び右に行く、その後、 右に行くです

そして、我々は今、 表現の面での宣言を考えているので、:式があまりにも読み取ることが複雑 になったとき、あなたは何をしますか?読みやすいように中間変数を の順に導入します。宣言の場合、 「中間変数」はtypedefです。特に、 は、戻り値の型の一部が 関数の引数(および他の多くの時間も)の後に終了すると、 は宣言をより簡単にするためにtypedefを使用する必要があると主張します。 ( は、「私が言うように、私はそうではありません」というルールですが、恐れ入りますが 時々、非常に複雑な宣言を使用します)

+0

"カッコがない場合は、まずすべてを左に処理し、次にすべてを右に処理することを意味します"。それは他の方法の周りになっていないのですか?あなたが言ったように、右のすべての演算子が左のすべての演算子よりも優先されます。だから、まず最初に - 右に、そして - に向かって。 – AnT

+0

@AnTはい。私は間違いなくその声明をミスタイプしました。私はそれを修正します、ありがとう。 (そして今まで誰もこのエラーに気付かなかったのは驚くべきことです)。 –