9

GTK+ コードでシグナル ハンドラーを設定するという問題に何度もぶつかりましたが、いくつかのパラメーターは必要なく、いくつかのシグナルのハンドラーとして同じ関数を使用したくなりました。そのハンドラーは異なるシグネチャーを持っていますが、最初の N 個の引数 (私が気にするもの)同じです。

関数が期待する引数よりも少ない引数を期待している場合、関数へのポインターを GObject API に渡すことは安全ですか (「私の PC で動作しますか?」というより実用的な意味ではなく、未定義の動作ではないという意味で)。実際に信号放出プロセスから取得しますか?

または、これを GTK+ から切り離すために、このコードは大丈夫ですか?

/* Note: No void *userdata argument! */
void show(int x) {
  printf("x = %d\n", x);
}

void do_stuff(void (*fn)(int, void *), void *userdata) {
  static int total = 0;
  (*fn)(total, userdata);
  total++;
}

void doitnow(void) {
  do_stuff(&show, NULL);
}

追加のクレジットとして、関数シグネチャと呼び出しサイトの間のさまざまな戻り値の型の意味について説明してください。

編集:ほぼ同じ質問が「互換性のある関数タイプ」をより綿密に調査し、GObjectシグナルハンドラーをチェーンするという私の具体的な問題に直接対処する答えを引き出します。TL;DR:はい、未定義の動作ですが、一部のツールキットでは (必須ではありませんが) 実際には慣用的です。

4

3 に答える 3

8

6.5.2.2、パラグラフ 9 によると、明示的に未定義の動作です。

関数が、呼び出された関数を示す式が指す (式の) 型と互換性のない型で定義されている場合、動作は未定義です。

showで定義される型、

void show(int x)

呼び出し元のポインターが指す式の型と互換性がない。

void (*fn)(int, void *)

また、明示的に 6.3.2.3、パラグラフ 8:

あるタイプの関数へのポインターは、別のタイプの関数へのポインターに変換され、再び元に戻される場合があります。結果は元のポインタと等しくなります。変換されたポインターを使用して、参照される型と互換性のない型を持つ関数を呼び出す場合、動作は未定義です。

関数の場合、「互換型」は 6.7.6.3 (15) [C99 の 6.7.5.3 (15)] で特徴付けられています。

2 つの関数型に互換性を持たせるには、両方で互換性のある戻り値の型を指定する必要があります。さらに、両方が存在する場合、パラメータ型リストは、パラメータの数と省略記号ターミネータの使用において一致する必要があります。対応するパラメータは、互換性のある型を持つ必要があります。一方の型にパラメーター型リストがあり、もう一方の型が、関数定義の一部ではなく、空の識別子リストを含む関数宣言子によって指定されている場合、パラメーター リストには省略記号終端記号がなく、各パラメーターの型はデフォルトの引数昇格を適用した結果の型と互換性があります。一方の型がパラメーター型リストを持ち、もう一方の型が (おそらく空の) 識別子リストを含む関数定義によって指定されている場合、両方のパラメーターの数が一致する必要があります。また、各プロトタイプ パラメータの型は、対応する識別子の型にデフォルト引数の昇格を適用した結果の型と互換性がなければなりません。(型の互換性と複合型の決定では、関数または配列型で宣言された各パラメーターは調整された型を持つと見なされ、修飾型で宣言された各パラメーターは宣言された型の非修飾バージョンを持つと見なされます。)

ここで最も直接的な部分は、引数の数が同じでなければならないということです。

于 2013-04-24T11:16:56.897 に答える
-1

http://www.unixwiz.net/techtips/win32-callconv-asm.htmlhttp://www.csee.umbc.edu/~chang/cs313.s02/stack.shtml を読んでください最初にパラメーターがプッシュされるため、問題は発生しません。したがって、必要のないものはすべて、呼び出し元スタックに残ります。しかし、逆の方法は確かに問題を引き起こします。

于 2013-04-24T11:17:10.840 に答える