12

最近、私は APUE を読んでいて、以下のように定義された関数を見つけました。

void (*signal(int signo, void (*func)(int)))(int);

私は混乱していました.signalは関数へのポインタであり、最後の(int)は彼のパラメータです。(int signo,void (*func)(int)) が何かわかりませんでした。

4

6 に答える 6

24

一般的な手順:左端の識別子を見つけて、作業を進めます。括弧、などの接尾辞演算子、および;などの単項演算子の前にバインドする明示的なグループ化は()ありません[]*したがって、次のことがすべて当てはまります。

T *x[N]             -- x is an N-element array of pointer to T
T (*x)[N]           -- x is a pointer to an N-element array of T
T *f()              -- f is a function returning a pointer to T
T (*f)()            -- f is a pointer to a function returning T

これらのルールを宣言に適用すると、次のように分類されます。

       signal                                      -- signal
       signal(                            )        -- is a function
       signal(    signo,                  )        -- with a parameter named signo 
       signal(int signo,                  )        --   of type int
       signal(int signo,        func      )        -- and a parameter named func
       signal(int signo,       *func      )        --   of type pointer
       signal(int signo,      (*func)(   ))        --   to a function
       signal(int signo,      (*func)(int))        --   taking an int parameter
       signal(int signo, void (*func)(int))        --   and returning void
      *signal(int signo, void (*func)(int))        -- returning a pointer
     (*signal(int signo, void (*func)(int)))(   )  -- to a function
     (*signal(int signo, void (*func)(int)))(int)  -- taking an int parameter
void (*signal(int signo, void (*func)(int)))(int); -- and returning void

つまり、をsignal返す関数へのポインタを返しますvoidsignal整数と、を返す別の関数へのポインタの2つのパラメータを取りますvoid

typedefを使用して、これを読みやすくすることができます(そして、signalUbuntu linuxのマニュアルページはまさにそれを行います)。ただし、構文がどのように機能するかを正確に示すために、typedefされていないバージョンを示すことは価値があると思います。typedef機能は素晴らしいですが、それを効果的に使用するには、基礎となる型がどのように機能するかを本当に理解する必要があります。

このsignal関数はシグナルハンドラーを設定します。2番目の引数は、信号を受信した場合に実行される関数です。現在のシグナルハンドラー(存在する場合)へのポインターが返されます。

たとえば、プログラムで割り込み信号(Ctrl-Cなど)を処理する場合は、次のようにします。

static int g_interruptFlag = 0;

void interruptHandler(int sig)
{
  g_interruptFlag = 1;
}

int main(void)
{
  ...
  /**
   * Install the interrupt handler, saving the previous interrupt handler
   */
  void (*oldInterruptHandler)(int) = signal(SIGINT, interruptHandler);

  while (!g_interruptFlag)
  {
    // do something interesting until someone hits Ctrl-C
  }

  /**
   * Restore the previous interrupt handler (not necessary for this particular
   * example, but there may be cases where you want to swap out signal handlers
   * after handling a specific condition)
   */
  signal(SIGINT, oldInterruptHandler);
  return 0;
}

編集私はサンプルコードを、signalうまくいけばもっと説明的なものに拡張しました。

于 2010-11-08T12:23:52.413 に答える
16
void (*signal(int signo, void (*func)(int)))(int);

signal は int を受け取る関数と、int を受け取って void を返す関数へのポインタであり、int を受け取って void を返す関数ポインタを返します。あれは、

typedef void(*funcPtr)(int)

それから私たちは持っています

funcPtr signal(int signo, funcPtr func); //equivalent to the above

構文は確かに奇妙であり、そのようなことは typedef で行う方がよいでしょう。例として、int を受け取り、char を受け取り double を返す関数へのポインターを返す関数を宣言する場合は、次のようになります。

double (*f(int))(char);

編集:「Wooooooow」と書かれたコメントの後に、もっと「woooow」な別の例を提供しています:)


1. それぞれが float を取り double を返す関数への 5 つのポインターの配列へのポインターを受け取る関数を宣言しましょう。
2. 4 つの int の配列を指す 3 つの配列へ
のポインタを返し、int を受け取る関数へのポインタを取り、float を取り void を返す関数へのポインタを返し、unsigned int を返す関数へのポインタを返します。

typedef ソリューションは次のようになります。

typedef double (*f1ptr) (float);
typedef f1ptr (*arr1ptr)[5];
typedef int (*arr2ptr)[4];
typedef arr2ptr (*arr3ptr)[3];
typedef void(*f2Ptr)(float);
typedef f2ptr (*f3ptr)(int);
typedef unsigned int (*f4ptr) (f3ptr);
f4ptr TheFunction(arr1ptr arg1, arr3ptr arg2);

さて、面白い部分:) typedefsなしでは、これは次のようになります:

 unsigned int (*TheFunction( double (*(*)[5])(float), int(*(*)[3])[4]))( void(*(*)(int))(float))

なんてこった、私はそれを書いたのですか?:)

于 2010-11-08T10:33:55.720 に答える
12

時計回りのスパイラル ルールが役に立ちます: http://c-faq.com/decl/spiral.anderson.html

次の 3 つの簡単な手順に従います。

未知の要素から始めて、らせん/時計回りの方向に移動します。次の要素に遭遇した場合、それらを対応する英語のステートメントに置き換えます。

[X] または [] => 配列 X のサイズ... または 配列の未定義のサイズ...

(type1, type2) => type1 と type2 を渡す関数...

  • =>へのポインタ...

すべてのトークンが覆われるまで、これをスパイラル/時計回りの方向に続けます。括弧内は常に最初に解決してください。

「例 #3: The 'Ultimate'」を参照してください。これは、まさにあなたが求めているものです。

「signal は、int を渡す関数と、int を渡して何も返さない (void) 関数へのポインタを渡す関数であり、int を渡して何も返さない (void) 関数へのポインタを返す」

于 2010-11-08T10:36:05.143 に答える
3

現在アクセスできない場合のためcdeclに、cdecl の出力を次に示します。

$ cdecl
cdecl> explain void (*signal(int , void (*)(int)))(int);
declare signal as function (int, pointer to function (int) returning void) returning pointer to function (int) returning void
于 2010-11-08T10:50:27.473 に答える
1

このサイトは、C意味不明の宣言を提供します:

C ちんぷんかんぷん <-> 英語

于 2010-11-08T10:50:10.910 に答える
0

ディストリビューションのcdeclをインストールする (利用可能な場合)、またはここに移動します

そうでなければ、Armen Tsirunyan の答えが正しいと思います。

于 2010-11-08T10:40:04.607 に答える