2012-01-23 8 views
14

可能性の重複:
What is the point of function pointers?関数ポインタの使い方は?

私は実用的なシナリオで関数ポインタが使用されているかを理解しようとしています。 誰も私に関数自体を別の関数の引数として渡す必要がある実用的な例を教えてください。

+0

これはC#ですが、C/C++と関数ポインタの実用性についても説明しています:http://stackoverflow.com/questions/667410/the-benefits-of-using-function-pointers – Algorhythm

答えて

18

ファンクションポインタは、callback mechanismを作成するときに便利であり、関数のアドレスを別の関数に渡す必要があります。

これらは、関数の配列を格納したり、たとえば動的に呼び出したりする場合にも便利です。

8

一般的な使用方法の1つはcallback functionです。

qsortライブラリ機能を使用して並べ替えを試みます。最後のパラメータは、あなたが書いたコンパレータ関数へのポインタです。

5

非常に便利なアプリケーションとして私の頭に浮かぶのはボタンです。次のコードを取る:

int buttonID = CreateButton ("Click Me!", 100, 100, 200, 100, onClick); 

これは、幅200、高さ100あなたがそれをクリックするたびに付き(100100)のボタンを作成し、onClickのが呼び出されます。

私は個人的なWindows APIラッパーで同様のものを使用します。それはボタンなどを作成するのがずっと簡単になります。

3

まあ、#1ストックの回答はqsortです。 qsortが使用する比較ルーチンは、関数ポインタとして渡されます。他の多くの「ジェネリックアルゴリズム」関数も同様の方法でコンパレータを使用します。例えばおそらくハッシュテーブルの実装があなたのハッシュ関数を受け入れるかもしれません。

C言語のGUIツールキットおよびアプリケーションフレームワーク(Gnome/Gtk +/Glibなど)は、関数呼び出しをタイマまたはユーザーインターフェイスイベントの「コールバック」として受け入れることがよくあります。 (EG:「このボタンがクリックされるたびにこの関数を呼び出す」または「...このタイマーが満了するたびに...」)

実際、Cのほとんどの「OOPのような」または「イベント駆動」のコードは、同様の理由。

4

関数ポインタのための2つの主要な用途があります。

  • コールバック - イベントハンドラのために使用され、パーサー専門、コンパレータ機能通過...
  • プラグインやエクステンション - プラグインが提供する関数へのポインタまたはライブラリ拡張は、標準機能(GetProcAddressdlsymなど)によって集められます。これらは、関数識別子を名前として受け取り、関数ポインタを返します。 OpenGLのようなAPIにとって絶対に重要です。
1

コールバックを関数に渡すために使用できます。たとえば、qsort()を使用して配列を並べ替えることができます。この機能は、独自のソート順序を使用できることを意味し、その引数の1つとして比較関数を取ります。ほとんどの場合、

// All odd numbers are before even numbers 
int cmpoddeven(const void *xp, const void *yp) { 
    int x = *((int*) xp); 
    int y = *((int*) yp); 
    if(x == y) 
    return 0; 
    if(x % 2 == y % 2) { 
    return (x < y ? -1 : 1); 
    if(x % 2 == 1) 
    return -1; 
    return 1; 
} 

int main() { 
    int array[] = {1, 2, 3, 4, 5}; 
    // calling qsort with cmpoddeven as the comparison function 
    qsort(array, 5, sizeof(int), &cmpoddeven); 
    // array == {1, 3, 5, 2, 4}; 
} 
1

が、それは本質的dependency inversionを行うためのCの方法です。 wikiの記事には、

と記載されています。下位レベルのモジュールに依存しないでください。どちらも抽象に依存するはずです。 B.抽象は細部に依存すべきではありません。詳細は抽象化に依存する必要があります。

古典的な例のqsortは、上位レベルのソート機能が、ソート対象のデータの型、サイズ、または比較方法に依存しないという意味でこれを行います。したがって、qsort()のint配列の場合、詳細はsizeof(int)と比較の実装です。抽象化は、任意のサイズの要素の配列と、その型の要素を比較する関数です。

参照:Inversion of Control

例としてpthread_create()を誰も言及していないことに私は驚いています。

私が考えることができる唯一の一般的な使用方法は、依存性反転として一般化することはできませんが、切り替え不可能なデータ型ではスイッチ状のフロー制御を実装しています。たとえば、文字列をオンにしたい場合は、ソートされた文字列キーを関数ポインタにマッピングしてバイナリ検索を行います。それはスイッチのようなO(1)ではなく、あなたがマッチを見つけるまで、盲目的にstrcmp()を大きなif-elseでやっているよりも優れています。しかし、文字列をトークン化して実際のスイッチを使用するよりも優れているとは限りません。

4

コールバックルーチンは、これまで説明した最も一般的なシナリオのようです。しかし、多くの他のものがあります...

有限状態マシン(多次元)配列の要素は、次の状態を処理/処理するルーチンを示します。これにより、FSMの定義が1つの場所(配列)に保持されます。

有効化機能と機能の無効化は、関数ポインタを使用して行うことができます。あなたは、同じまたは別個のことをする機能を有効または無効にしたい場合があります。 if-elseを使用してコードを入力および乱雑にする代わりに、関数ポインタを使用するようにコードを作成し、関数ポインタを変更または割り当てて機能を有効または無効にすることができます。新しいバリアントを追加すると、if-elseまたはスイッチのすべてのケースを追跡する必要がなくなり、リスクケースがなくなる危険性もありません。新しい機能を有効にするために関数ポインタを更新するか、古い機能を無効にするだけです。

コードクラッタを減らす私は前の例でこれに触れました。例のような...

switch (a) { 
case 0: 
    func0(); 
    break; 
case 1: 
    func1(); 
    break; 
case 2: 
    func2(); 
    break; 
case 3: 
    func3(); 
    break; 
default: 
    funcX(); 
    break; 
} 

は多くがよりありへ...

/* This declaration may be off a little, but I am after the essence of the idea */ 
void (*funcArray)(void)[] = {func0, func1, func2, func3, funcX}; 
... appropriate bounds checking on 'a' ... 
funcArray[a](); 

を簡略化することができます。お役に立てれば。

+1

FWIW、宣言'void(* funcArray [])(void)= {func0、func1、func2 ...でなければなりません}; '(' funcArray'は関数へのポインタの配列です...)。宣言が使用を模倣していることを忘れないでくださいコード内の式が 'funcArray [a]()'である場合、宣言は同じように構造化されます。 –

+1

@JohnBode - 非常に感謝しています。私はC言語で20年以上働いていますが、私はまだ関数ポインタの構文が混ざっています。通常は、関数ポインタを最初にtypedef'ingしてからtypedef'ed変数の配列を宣言する方が簡単です(私にとって)。 – Sparky

+0

@ Sparky typedefについて合意しました。これは、関数ポインタの配列や関数ポインタへのポインタのような状況を読みやすくする。 –