Cで関数をパラメーターとしてどのように渡しますか?


616

一連のデータに対してパラメーターによって渡される関数を実行する関数を作成したいと思います。Cで関数をパラメーターとしてどのように渡しますか?


12
関数/配列を変数として使用している場合は、常にを使用しますtypedef
Mooing Duck


関数名は、関数のポンターである必要があります。Cを学ぶほとんどの人は遅かれ早かれqsortをカバーしますが、これは正確にこれを行うのですか
mckenzm 2015

5
@MooingDuck私はあなたの提案に同意しません、そしてあなたの提案は結論を支持する理由が欠けています。私は個人的には関数ポインターでtypedefを使用しないことを好みます。これにより、コードがより明確で読みやすくなります。
andrewrk

2
@andrewrk:あなたは好むvoid funcA(void(*funcB)(int))void (*funcA())()するtypedef void funcB(); void funcA(funcB)funcB funcA()?上向きに見えない。
Mooing Duck

回答:


747

宣言

関数パラメーターを受け取る関数のプロトタイプは次のようになります。

void func ( void (*f)(int) );

これは、パラメータfvoid戻り値の型を持ち、単一のintパラメータを取る関数へのポインタになることを示しています。次の関数(printfuncは、適切なタイプであるためパラメーターとして渡すことができる関数の例です。

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);
  }
}

ソース


40
最初と最後のコード例では、*は必須ではありません。関数パラメーター定義とf関数呼び出しはどちらも、f*なしでそのまま使用できます。ただし、パラメーターfが関数ポインターであることを明確にするために、これを同じように実行することをお勧めします。ただし、読みやすさが損なわれることがよくあります。
Gauthier

5
例については、[c99、6.9.1§14]を参照してください。もちろんどちらも正しいですが、私は代わりに言及したかっただけです。
Gauthier

6
本当に?トップ評価の答えはtypedef、関数ポインタの使用への単一の参照を行いませんか?申し訳ありませんが、反対票を投じる必要があります。
Jonathon Reinhart、2014年

3
@JonathonReinhart、「typedef」アプローチの利点は何ですか?ただし、このバージョンは見た目がすっきりしており、余分なステートメントもありません。ここでかなりスターター。
Abhinav Gauniyal 2015

3
@JonathonReinhartはよく見つかりました。ポインタのtypedefはコードを難読化するため、使用しないでください。
MM

129

この質問にはすでに関数ポインタを定義するための答えがありますが、特にアプリケーションの周りでそれらを渡す場合は、非常に面倒になる可能性があります。この不快さを回避するために、関数ポインターをより読みやすいものに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;

上記よりもはるかに優れています。


4
「関数ポインタを型に変換したい」場合は、boostライブラリは必要ありません。使用するだけtypedefです。それはより簡単で、追加のライブラリを必要としません。
wizzwizz4

72

C ++ 11以降では、関数ライブラリを使用して、これを簡潔かつ一般的な方法で行うことができます。構文は、例えば、

std::function<bool (int)>

ここでboolは、最初の引数がtypeである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);
}

43
問題はCについてです。C ++はここでは適用されません。
スーパーキャット

16
C ++に関する質問(stackoverflow.com/questions/6339970/…)はここで参照されているので、この回答は適切だと思います。
mr_T 2016年

8
以下のため、この質問がCであるので多分C ++のための質問はここに参照すべきではない
マイケル・フルトン

5
この質問は、「パラメーターとしてのc ++関数呼び出し」を検索したときに最初に出てきたので、この回答はここでの目的に関係なく役立ちます。
ufoxDan

25

以下に示すように、関数のアドレスをパラメーターとして別の関数に渡します

#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();
}

1
非常に良い答えです。コードのブロック全体を使用します(すべてをわかりにくい混乱にスライスするのではなく)。両方の手法の違いについて詳しく教えてください。
Rafael Eyng

5

ISO C11 6.7.6.3p8に従って、関数を関数ポインターとして「渡す」ことができます:「パラメーターを「関数の戻り値の型」として宣言すると、 6.3のように、「関数の戻り値の型へのポインター」に調整されます。 .2.1。 " たとえば、これ:

void foo(int bar(int, int));

これと同等です:

void foo(int (*bar)(int, int));

どのドキュメントから引用していますか?それへのリンクはありますか?
Rafael Eyng

1
ISO C11標準から引用しています。
doppelheathen


-2

実際には関数ではありませんが、ローカライズされたコードです。もちろん、結果だけをコードに渡すわけではありません。後で実行するためにイベントディスパッチャーに渡された場合は機能しません(イベントが発生したときではなく、結果がすぐに計算されるため)。しかし、それがあなたがやろうとしているすべてのことなら、それはあなたのコードを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;
}

関数を呼び出すだけです。代わりに他の関数をどのように渡しIncMultIntますか?
Tejas Pendse
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.