2013-04-24 6 views
9

私は、GTK +コードでシグナルハンドラを設定するという問題に何度もぶつかりつつあります。いくつかのパラメータを必要とせず、ハンドラのシグネチャが異なる複数のシグナルのハンドラと同じ関数を使いたいと思います。議論(私が気にすること)は同じです。Cの関数を、予想以上の引数で呼び出しても安全ですか?

GObject APIに関数へのポインタを渡すのは、実際の意味ではなく、未定義の動作ではありません。議論はシグナル放出プロセスから実際に得られるよりも?

または、これを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); 
} 

さらに詳しい情報については、関数シグネチャとコールサイトの間で異なる戻り値の型がどのような意味を持つかを検討してください。

編集:より密接almost-identical questionプローブ「互換性のある機能の種類」、およびan answer directly addressing my concrete problemを描く - GObjectの信号ハンドラを連鎖すること。 TL; DR:はい、それは未定義の動作ですが、実際には(いくつかのツールキットでは必須ではありませんが)慣用的です。

+0

このように関数ポインタをUBに変換することはほぼ確実です。 – Flexo

+3

@Flexo関数ポインタを変換しても問題ありません。これを逆変換以外の目的で使うと、... –

+0

ところで、do_stuffでは '(* fn)'は必要ありません。 'fn(total、userdata)'を呼び出すだけです。 'fn'は関数ポインタであるため、関数呼び出し演算子'(total、userdata) 'は、あなたが期待するものとまったく同じです。 – Jens

答えて

8

それは、6.5.2.2ごとに、明示的にパラグラフ9未定義の動作です:関数は(式の)タイプと互換性がありません型で定義されている場合は

を表す表現で指さ呼び出された関数の動作は未定義です。

showを用いて定義されているタイプ、

void show(int x) 

式のタイプと互換性がないが

void (*fn)(int, void *) 

また明示的に、それが呼び出され、それを通してポインタによって指さ6.3.2.3段落8:

ポイント1つの型の関数へのポインタは、別の型の関数へのポインタに変換され、再び戻される可能性があります。結果は元のポインタと等しいとみなされます。 変換されたポインタを使用して、その型が参照された型と互換性のない関数を呼び出す場合、その動作は未定義です。 [C99で6.7.5.3(15)]機能について

、 "互換性のあるタイプは" 6.7.6.3を特徴とする(15)2つの関数の種類について

適合するために、両方のものと互換性のある戻り値の型を指定する。さらに、両方とも存在する場合、パラメータタイプリストは、パラメータの数および省略記号ターミネータの使用において一致しなければならない。対応するパラメータは互換性のある型を持つものとする。 1つの型がパラメータ型リストを持ち、もう1つの型が関数定義の一部ではなく、空の識別子リストを含む関数宣言子によって指定されている場合、パラメータリストは省略記号を持たず、各パラメータの型は既定の引数のプロモーションを適用した結果の種類と互換性があります。1つの型がパラメータ型リストを持ち、もう1つの型が(恐らく空の)識別子リストを含む関数定義によって指定された である場合、両者はパラメータ数で一致し、各プロトタイプパラメータの型は対応する識別子の型へのデフォルトの引数プロモーションの適用から生じる型。 (型互換性および複合型の判定では、関数または配列型で宣言された各パラメータは調整された型とみなされ、修飾型で宣言された各パラメータは宣言型の非修飾型とみなされます)

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

+0

あなたはそんなに早く検索しましたか?あなたの指のヒントで標準を知っていますか?:-) –

+0

これはいいですが、私はこの場合にどこを見るかを知っていました。 –

+0

あなたの答えは、答えの残りの半分、つまり関数に関連する「互換性のある型」の意味を見つけるのを助けました。私がこれを受け入れる前に、ここにそれを追加しますか?明らかに6.7.5.3§15です。 –

-1

読み取りhttp://www.unixwiz.net/techtips/win32-callconv-asm.htmlhttp://www.csee.umbc.edu/~chang/cs313.s02/stack.shtmlさらに多くを渡す場合、最初にパラメータがプッシュされているので問題はありません。したがって、必要でないものは、呼び出し元のスタックにとどまります。しかし、逆の方法は必ず問題を引き起こすでしょう。

+0

これは、いくつかの実装で発生する可能性がありますが、それは標準ごとに未定義の動作であるとは変わりません。 – Flexo

+0

呼び出し元がスタックをクリーンアップする場合は問題ありませんが、呼び出し先はどうですか?オーバーサイズパラメータスタックフレームのために間違ったパラメータを取得しませんか? –

+0

注文が正しいパラメータ内で正しい場合、残りのパラメータは実際にスタックフレーム内の戻りアドレスの下にある基本ポインタの下にあるパラメータの下にあります。したがって、呼び出し先がパラメータを読み込んでいるときは、それが読み込まれるべきものを読み込みます。残りはそのままの状態で横たわっています。しかし、被呼者が読まれるはずの人の間で、何かが間違っていれば、問題が生じるでしょう。 – abasu

2

これはC標準では未定義の動作ですが、C標準では可変引数を持つ関数のサポートも必須で、コンパイラが実装する唯一の方法は前者を許可することです。

この特定の慣用形式は、GLibでサポートされているすべてのコンパイラとプラットフォームでサポートされており、GLibを作成する前から長年にわたり使用されています。

+0

Counterpoint:20年前、私はMS QuickCを使いました。これは、 "パスカル"呼び出し規約(スタック上の引数の順番を逆にする - 最初の引数を最後よりも最初に押す)を使用するように指示できました。そのような場合に引数の数を間違えると、破損するでしょう。もちろん、ツールキットは、これが問題となるプラットフォームを自由に定義し、そうでないプラットフォームのみをサポートします。 –

+0

だから、GLibが何らかの方法で他の言語をエミュレートすることを考えようとしていた何年も前に、GLibによって何らかの形でサポートされていないプラットフォーム上の*非常に*古いコンパイラに伝えました。これは問題になるだろう...どのように? :-) – ebassi

関連する問題