C で関数をパラメータとして渡すにはどうすればよいですか?
-
08-06-2019 - |
質問
データのセットに対してパラメータによって渡される関数を実行する関数を作成したいと考えています。C で関数をパラメータとして渡すにはどうすればよいですか?
解決
宣言
関数パラメータを取る関数のプロトタイプは次のようになります。
void func ( void (*f)(int) );
これは、パラメータが f
を持つ関数へのポインタになります。 void
戻り値の型であり、単一の値を受け取ります int
パラメータ。次の関数 (print
) に渡すことができる関数の例です。 func
これは適切な型であるため、パラメータとして使用します。
void print ( int x ) {
printf("%d\n", x);
}
関数呼び出し
関数パラメーターを使用して関数を呼び出す場合、渡される値は関数へのポインターである必要があります。これには、関数の名前 (かっこなし) を使用します。
func(print);
電話するだろう func
, 、それに print 関数を渡します。
関数本体
他のパラメーターと同様に、 func は関数本体でパラメーターの名前を使用してパラメーターの値にアクセスできるようになりました。func は、渡された関数を 0 ~ 4 の数値に適用するとします。まず、print を直接呼び出すループがどのようになるかを考えてみましょう。
for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
print(ctr);
}
以来 func
のパラメータ宣言は次のように述べています f
は目的の関数へのポインタの名前です。最初に次のことを思い出してください。 f
ポインタです *f
それは f
を指します(つまり、関数 print
この場合)。その結果、上記のループ内の print をすべて次のように置き換えるだけです。 *f
:
void func ( void (*f)(int) ) {
for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
(*f)(ctr);
}
}
から http://math.hws.edu/bridgeman/courses/331/f05/handouts/c-c++-notes.html
他のヒント
この質問には関数ポインタの定義に関する答えがすでにありますが、特にアプリケーション内で関数ポインタを渡す場合、関数ポインタは非常に面倒になる可能性があります。この不快さを避けるために、関数ポインタをより読みやすいものに typedef することをお勧めします。例えば。
typedef void (*functiontype)();
void を返し、引数を取らない関数を宣言します。この型への関数ポインターを作成するには、次のようにします。
void dosomething() { }
functiontype func = &dosomething;
func();
intを返し、charを受け取る関数の場合は、次のようにします
typedef int (*functiontype2)(char);
そしてそれを使うために
int dosomethingwithchar(char a) { return 1; }
functiontype2 func2 = &dosomethingwithchar
int result = func2('a');
関数ポインターを読みやすい型に変換するのに役立つライブラリがあります。の ブースト機能 ライブラリは素晴らしいので、努力する価値は十分にあります。
boost::function<int (char a)> functiontype2;
上記よりもはるかに優れています。
C++11以降では、 関数ライブラリ これを簡潔かつ一般的な方法で行うためです。構文は次のようになります。
std::function<bool (int)>
どこ bool
ここでの戻り値の型は、最初の引数が次の型である 1 つの引数を持つ関数の戻り値の型です。 int
.
以下にサンプルプログラムを含めました。
// g++ test.cpp --std=c++11
#include <functional>
double Combiner(double a, double b, std::function<double (double,double)> func){
return func(a,b);
}
double Add(double a, double b){
return a+b;
}
double Mult(double a, double b){
return a*b;
}
int main(){
Combiner(12,13,Add);
Combiner(12,13,Mult);
}
ただし、場合によっては、テンプレート関数を使用した方が便利な場合があります。
// g++ test.cpp --std=c++11
template<class T>
double Combiner(double a, double b, T func){
return func(a,b);
}
double Add(double a, double b){
return a+b;
}
double Mult(double a, double b){
return a*b;
}
int main(){
Combiner(12,13,Add);
Combiner(12,13,Mult);
}
合格 別の関数へのパラメータとしての関数のアドレス 以下に示すように
#include <stdio.h>
void print();
void execute(void());
int main()
{
execute(print); // sends address of print
return 0;
}
void print()
{
printf("Hello!");
}
void execute(void f()) // receive address of print
{
f();
}
また、次を使用して関数をパラメータとして渡すこともできます 関数ポインタ
#include <stdio.h>
void print();
void execute(void (*f)());
int main()
{
execute(&print); // sends address of print
return 0;
}
void print()
{
printf("Hello!");
}
void execute(void (*f)()) // receive address of print
{
f();
}
を渡す必要があります 関数ポインタ. 。構文は少し面倒ですが、慣れてしまえば非常に強力です。
6.7.6.3p8 に従って、関数は関数ポインターとして「渡す」ことができます。」「関数を返す型」としてのパラメータの宣言は、6.3.2.1 のように、「関数を返す型へのポインタ」に調整されます。 」。たとえば、これは次のとおりです。
void foo(int bar(int, int));
はこれと同等です:
void foo(int (*bar)(int, int));
これは実際には関数ではありませんが、ローカライズされたコードです。もちろん、コードは渡されず、結果だけが渡されます。後で実行するためにイベント ディスパッチャーに渡した場合は機能しません (結果はイベントの発生時ではなく現在計算されるため)。ただし、それだけを行おうとしている場合は、コードを 1 か所にローカライズします。
#include <stdio.h>
int IncMultInt(int a, int b)
{
a++;
return a * b;
}
int main(int argc, char *argv[])
{
int a = 5;
int b = 7;
printf("%d * %d = %d\n", a, b, IncMultInt(a, b));
b = 9;
// Create some local code with it's own local variable
printf("%d * %d = %d\n", a, b, ( { int _a = a+1; _a * b; } ) );
return 0;
}