55

この質問はそれほど具体的ではありません。それは本当に私自身の C エンリッチメントのためであり、他の人にも役立つことを願っています。

免責事項: 多くの人が「FP を実行しようとしている場合は、関数型言語を使用してください」という衝動に駆られることを私は知っています。私は、他の多くの C ライブラリにリンクする必要がある組み込み環境で作業しており、さらに多くの大規模な共有ライブラリ用のスペースがなく、多くの言語ランタイムをサポートしていません。さらに、動的メモリ割り当ては問題外です。私もとても興味があります。

私たちの多くは、ラムダ式用のこの気の利いた C マクロを見たことがあるでしょう。

#define lambda(return_type, function_body) \
({ \
      return_type __fn__ function_body \
          __fn__; \
})

使用例は次のとおりです。

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
max(4, 5); // Example

を使用するgcc -std=c89 -E test.cと、ラムダは次のように展開されます。

int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });

だから、これらは私の質問です:

  1. 行int (*X);は正確には何をしますか。宣言する?もちろん、int * X; は整数へのポインタですが、これら 2 つの違いは何ですか?

  2. 展開されたマクロを見ると、ファイナルは一体何をしているの__fn__だろうか?テスト関数を作成するvoid test() { printf("hello"); } test;と、すぐにエラーがスローされます。その構文がわかりません。

  3. これはデバッグにとって何を意味するのでしょうか? (私はこれと gdb で自分自身を実験する予定ですが、他の人の経験や意見は素晴らしいでしょう)。これは静的アナライザーを台無しにしますか?

4

4 に答える 4

42

この宣言 (ブロック スコープで):

int (*max)(int, int) =
    ({
    int __fn__ (int x, int y) { return x > y ? x : y; }
    __fn__;
    });

C ではありませんが、有効な GNU C です。

gcc次の 2 つの拡張機能を使用します。

  1. ネストされた関数
  2. ステートメント式

ネストされた関数(複合ステートメント内で関数を定義する) とステートメント式(({})基本的には値を生成するブロック) は C では許可されておらず、GNU C に由来します。

ステートメント式では、最後の式ステートメントが構造の値です。これが、ネストされた関数__fn__がステートメント式の最後に式ステートメントとして表示される理由です。式中の関数指定子 (__fn__最後の式ステートメント内) は、通常の変換によって関数へのポインターに変換されます。これは、関数ポインタを初期化するために使用される値maxです。

于 2012-05-01T23:04:24.323 に答える
6

あなたのラムダ マクロは、2 つのファンキーな機能を利用しています。最初に、ネストされた関数を使用して実際に関数の本体を定義します (したがって、ラムダは実際には匿名ではなく、暗黙の__fn__変数を使用するだけです (先頭の二重アンダースコア名はコンパイラ用に予約されているため、別の名前に変更する必要があります)。だから多分何かのようなyourapp__fn__方が良いでしょう)。

これらすべては、GCC 複合ステートメント ( http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprsを参照) 内で実行され、その基本的な形式は次のようになります。

({ ...; retval; })

複合ステートメントの最後のステートメントは、宣言されたばかりの関数のアドレスです。これint (*max)(int,int)で、複合ステートメントの値が割り当てられるだけになりました。これは、宣言されたばかりの「匿名」関数へのポインターになりました。

もちろん、マクロのデバッグは大変な作業です。

test;なぜ..少なくともここで、「テストが異なるタイプのシンボルとして再宣言された」という理由については、GCCがそれを(役に立たない)式ではなく宣言として扱っていることを意味すると思います。型指定されていない変数のデフォルトは でintあり、すでにtest関数として宣言されているため (本質的にはvoid (*)(void))、それが得られます..しかし、私はそれについて間違っている可能性があります。

ただし、これはいくら想像しても移植可能ではありません。

于 2012-05-01T23:23:29.157 に答える
1

部分的な回答: あなたが興味を持っているのは int(*X) ではありません。int (*X)(y,z) です。これは、(y,z) を受け取って int を返す X という関数への関数ポインタです。

デバッグの場合、これは非常に困難です。ほとんどのデバッガーは、マクロをトレースできません。ほとんどの場合、アセンブリをデバッグする必要があります。

于 2012-05-01T22:51:50.377 に答える
0
  1. int (*max)(int, int)宣言する変数の型です。これは、int を返す max という名前の関数ポインターとして定義され、パラメーターとして 2 つの int を取ります。

  2. __fn__関数名を参照します。この場合は max です。

  3. そこに答えはありません。プリプロセッサを介して実行した場合は、ステップスルーできると思います。

于 2012-05-01T22:50:18.633 に答える