関数へのポインタのソースを表示
←
関数へのポインタ
ナビゲーションに移動
検索に移動
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループに属する利用者のみが実行できます:
登録利用者
。
このページのソースの閲覧やコピーができます。
'''関数へのポインタ''' (かんすうへのポインタ、{{lang-en-short|pointer to function}}) あるいは'''関数ポインタ''' (かんすうポインタ、{{lang-en-short|function pointer}}) は、[[C言語]], [[C++]], [[D言語]]やその他多くのプログラミング言語における[[ポインタ (プログラミング)|ポインタ]]の一種である。関数へのポインタを[[デリファレンス]] (dereference) すれば、そのポインタが指し示す'''関数'''([[サブルーチン]])を呼び出せる。応用例としては、[[switch文]]を置き換えるテーブルジャンプを実装する、[[コールバック (情報工学)|コールバック]]関数によるカスタマイズポイントを提供する、などといったものがある。 '''[[関数オブジェクト]]''' ({{lang-en-short|function object}}) は、関数へのポインタに似ているが、コード領域中のエントリポイントを指す単なるポインタである関数へのポインタと違い、データ領域上に実体を持つ[[オブジェクト (プログラミング)|オブジェクト]]であるという点が異なっている(実装の詳細は言語や処理系により異なるが)。そのため、関数オブジェクトはデータを保持でき、[[クロージャ]]を再現することもできる。ゆえに、関数オブジェクトは、「関数へのポインタ」ではなく「関数」という型と値を持つようなものと言え、より強力である。 [[C Sharp|C#]]や[[Microsoft Visual Basic .NET|Visual Basic .NET]]などといった[[.NET Framework]]用の言語には、[[メソッド (計算機科学)|メソッド]]を参照する型として、[[デリゲート (プログラミング)|デリゲート]]がある。[[P/Invoke]]などの.NET相互運用において、デリゲートは関数へのポインタにマーシャリングされる<ref>[https://docs.microsoft.com/ja-jp/dotnet/framework/interop/default-marshaling-behavior 既定のマーシャリングの動作 | Microsoft Docs]</ref>。 [[Java]]はバージョン8でメソッド参照を導入し、関数ポインタやデリゲート類似の機能を利用できるようになったが、バージョン7まではメソッド参照を持たず、代替として[[メソッド (計算機科学)|メソッド]]を1つだけ持つ[[インタフェース (抽象型)|インタフェース]]を利用して同等機能を実現する必要がある。 [[第一級オブジェクト]]として関数を使用できる([[第一級関数]]がある)言語では、関数も引数で渡したり、戻り値で返したり、他の関数から動的に作成したりできるなどデータ同様に扱えるため、関数へのポインタは必要とされない。 ==Cでの例== 以下の例では、関数へのポインタとして<code>func_ptr</code>が宣言され、そこへ関数<code>my_function</code>のアドレスを割り当てている。そして<code>func_ptr</code>を通じて関数を呼び出している。 <syntaxhighlight lang="c"> #include <stdio.h> static int my_function(int a) { printf("my_function: %d\n", a); return 2 * a + 3; } int main(void) { int (*func_ptr)(int a) = my_function; /* あるいは以下でも可能 */ /* int (*func_ptr)(int) = &my_function; */ int x; x = (*func_ptr)(10); /* あるいは以下でも可能 */ /* x = func_ptr(10); */ printf("main: %d\n", x); return 0; } </syntaxhighlight> 注1: 関数シンボル<code>f</code>自体は関数型 (function type) であり、関数型は関数ポインタとは異なる。単項の[[アドレス演算子]] (address operator) <code>&</code>を付与した式<code>&f</code>は関数ポインタを返す。しかし、関数型の式<code>f</code>は関数へのポインタに暗黙変換されるため、アドレス演算子を適用せずとも関数へのポインタに代入可能である。 注2: 関数へのポインタ<code>fp</code>に単項の[[間接演算子]] (indirection operator) <code>*</code>を付与した式<code>*fp</code>は関数指示子<ref>あるいは関数指定子とも。[https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_72/rzarg/lvalue.htm IBM Knowledge Center - 左辺値と右辺値]</ref> (function designator) となる。<code>(*fp)(arg)</code>という構文は、関数へのポインタ<code>fp</code>を通じて関数を呼び出す構文である。しかしCでは<code>fp(arg)</code>という構文も認められている<ref>{{Cite web |last=Summit |first=Steve |coauthors=北野 欽一 |date=1996-02-26 |url=http://www.kouno.jp/home/c_faq/c4.html#12 |title=C FAQ 4 |work=4.12: 関数を呼ぶのに、ポインターを通す方法をみたことがある。どうなってるの。 |accessdate=2008-10-14 }} </ref>。 注3: 関数ポインタの宣言における仮引数リストは、型が一致してさえいればいいので、仮引数の名前は省略できる。 次の例では、関数へのポインタを引数として他の関数に渡している。ここでは、関数<code>my_function</code>が、先の例のように関数へのポインタを通じて呼び出される。関数<code>caller</code>は、引数として関数へのポインタと整数値を1つ取る。引数の整数値は、その関数へのポインタを通じて関数を呼び出すときに渡す引数として用いられる。そこで宣言されている関数へのポインタの[[プロトタイプ宣言|プロトタイプ]]に適合しさえすれば、<code>caller</code>の第1引数には、どんな関数でも渡すことが可能である。 <syntaxhighlight lang="c"> #include <stdio.h> static void my_function(int a) { printf("my_function: %d\n", a); } static void caller(void (*func_ptr)(int a), int p) { (*func_ptr)(p); } int main(void) { caller(my_function, 10); return 0; } </syntaxhighlight> 3番目の例でも関数へのポインタを引数として他の関数に渡して用いている。関数<code>f</code>は、指定された区間で[[積分]]<math>\textstyle\int_{a}^{b} f(x)\;dx</math>の近似を計算する関数<code>integ</code>へ渡されている。<math>x</math>における<math>f(x)</math>の値を求めるために、<code>f(x)</code>が<code>integ</code>から呼び出されている。<code>integ</code>では、「<code>double</code>型の引数を1つ取り、<code>double</code>型の値を返す関数」でありさえすれば、どんな関数でも計算させることが可能である。 なお、関数へのポインタを定義する際には、事前に[[typedef]]を用いて関数型もしくは関数ポインタ型のエイリアスを定義しておくと便利である。 <syntaxhighlight lang="c"> #include <stdio.h> #include <math.h> #define PI 3.14159265358979323846 typedef double (*fx_ptr_t)(double x); /* あるいは以下でも可能 */ /* typedef double fx_t(double x); typedef fx_t* fx_ptr_t; */ double integ(double a, double b, fx_ptr_t fp) { double sum = 0.0; double x; int n; /* 積分 {a,b} f(x) dx の計算 */ for (n = 0; n <= 100; ++n) { x = (n / 100.0) * (b - a) + a; sum += (*fp)(x) * (b - a) / (100.0 + 1.0); } return sum; } int main(void) { int i; struct pair { fx_ptr_t fp; const char* name; } pairs[3] = { { cos, "cos" }, { sin, "sin" }, { tan, "tan" }, }; printf("From 0 to pi/4:\n"); for (i = 0; i < 3; ++i) { printf("\t" "Integral of %s = %g\n", pairs[i].name, integ(0, PI/4, pairs[i].fp)); } return 0; } </syntaxhighlight> 関数ポインタを引数として渡すことで、処理を外部から組み込む(カスタマイズする)ことが可能である。関数ポインタを受け取って関数を呼び出す側は、その関数内で何が実行されるかを関知する必要がない。引数として渡される関数は、'''コールバック関数'''と呼ばれることもある。主に[[イベント (プログラミング)|イベント]]処理や、判断を外部に任せて処理を行なうときなどに用いられる。 == C++での例 == C++において、以下の関数へのポインタは、Cの関数へのポインタとの互換性があり、[[呼び出し規約]]が同じであれば直接の相互運用が可能である。 * クラスに属さない名前空間レベルのグローバル関数(フリー関数) * クラスの静的メンバー関数 一方で、クラスの非静的メンバー関数へのポインタは互換性がない。また、非静的メンバー関数へのポインタを経由した関数の呼び出しには、そのクラスのインスタンスが必要となる。 C++では関数への参照を定義することもできるが、非静的メンバー関数への参照を定義することはできない<ref>ISO/IEC 14882 | 8.3.3 Pointers to members</ref>。 <syntaxhighlight lang="c++"> #include <cstdio> void GlobalFunc1() { puts("GlobalFunc1"); } class MyClass { public: static void ClassFunc1() { puts("MyClass::ClassFunc1"); } void InstanceFunc1() { puts("MyClass::InstanceFunc1"); } }; int main() { void(*fpGlobalFunc1a)() = GlobalFunc1; void(*fpGlobalFunc1b)() = &GlobalFunc1; void(&frGlobalFunc1)() = GlobalFunc1; void(*fpClassFunc1a)() = MyClass::ClassFunc1; void(*fpClassFunc1b)() = &MyClass::ClassFunc1; void(&frClassFunc1)() = MyClass::ClassFunc1; fpGlobalFunc1a(); fpGlobalFunc1b(); frGlobalFunc1(); (*fpGlobalFunc1a)(); (*fpGlobalFunc1b)(); (*frGlobalFunc1)(); fpClassFunc1a(); fpClassFunc1b(); frClassFunc1(); (*fpClassFunc1a)(); (*fpClassFunc1b)(); (*frClassFunc1)(); void(MyClass::*fpInstanceFunc1)() = &MyClass::InstanceFunc1; MyClass obj; (obj.*fpInstanceFunc1)(); return 0; } </syntaxhighlight> C++では関数テンプレートの述語 (predicate) として通例[[関数オブジェクト]] (functor) あるいは[[ラムダ式]] (lambda expression) が渡されるが、関数ポインタを渡すことも可能である。ただし、関数オブジェクトやラムダ式のほうが[[コンパイラ最適化]]によるインライン化が期待できるため好まれる。なお、ラムダ式は[[C++11]]以降で標準化された機能であるが、[[Boost C++ライブラリ]]のBoost.Lambdaを利用することで、C++03以前でもラムダ式を利用することができる。 <syntaxhighlight lang="c++"> #include <iostream> #include <vector> #include <algorithm> bool CompareFuncGreater(const int& a, const int& b) { return a > b; } void PrintLine(int x) { std::cout << x << std::endl; } int main() { // 降順にソート。 std::vector<int> data { 5, 24, 1, -12, 0, 8, -7 }; #if 0 std::sort(data.begin(), data.end(), std::greater<int>()); std::for_each(data.begin(), data.end(), [](int x) { std::cout << x << std::endl; }); #else std::sort(data.begin(), data.end(), CompareFuncGreater); std::for_each(data.begin(), data.end(), PrintLine); #endif return 0; } </syntaxhighlight> また、C++11以降ではtypedefの代わりにusingを使うこともできる。typedefよりもusingのほうがいくらか分かりやすい構文になっている。 <syntaxhighlight lang="c++"> typedef double (*fx_ptr_t)(double x); </syntaxhighlight> 下記は上記の[[糖衣構文]]である。 <syntaxhighlight lang="c++"> using fx_ptr_t = double (*)(double x); </syntaxhighlight> ==脚注== {{脚注ヘルプ}} <references/> ==関連項目== * [[間接参照]] * [[第一級関数]] * [[高階関数]] * [[ポインタ (プログラミング)]] * [[デリゲート (プログラミング)]] ==外部リンク== *[http://www.cplusplus.com/doc/tutorial/pointers.html Pointer Tutorials], C++ documentation and tutorials *[http://www.newty.de/fpt/ Function Pointer Tutorials], a Guide to C/C++ function pointers {{DEFAULTSORT:かんすうへのほいんた}} [[Category:プログラミング言語の構文]] [[de:Zeiger (Informatik)#Funktionszeiger (Methodenzeiger)]]
このページで使用されているテンプレート:
テンプレート:Cite web
(
ソースを閲覧
)
テンプレート:Lang-en-short
(
ソースを閲覧
)
テンプレート:脚注ヘルプ
(
ソースを閲覧
)
関数へのポインタ
に戻る。
ナビゲーション メニュー
個人用ツール
ログイン
名前空間
ページ
議論
日本語
表示
閲覧
ソースを閲覧
履歴表示
その他
検索
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
特別ページ
ツール
リンク元
関連ページの更新状況
ページ情報