2016-11-10 8 views
1

How do function pointers in C work?への回答の1つでは、戻り値に関数ポインタを使用する方法について説明しています。ここで問題のコードは次のとおりです。私にとってはCの構文で関数へのポインタを返す

// this is a function called functionFactory which receives parameter n 
// and returns a pointer to another function which receives two ints 
// and it returns another int 

int (*functionFactory(int n))(int, int) { 
    printf("Got parameter %d", n); 
    int (*functionPtr)(int,int) = &addInt; 
    return functionPtr; 
} 

あなたはfunctionFactory機能は非常に奇妙である宣言する方法 - あなたが戻り値の型(関数へのポインタ)と、関数名自体(functionFactoryをミックスように私には思えます)。例えば

、私たちはその引数の二乗を返す単純な関数を書くときに我々は明らかに、我々は返すものの種類が左側にある

int square(int n){ 
    return n*n; 
} 

のようなものを書いて、それから、我々は、関数名を書きます次にそれが受け入れるパラメータ。ここで

(int (*function)(int, int)) functionFactory(int n) { ... 

リターン(関数へのポインタである)の種類とその内容(例えば、我々が何を指している機能:我々は、関数へのポインタを返すときに、私たちは次のように書いていない理由戻り値とパラメータとして受け入れるもの)が明確に左に区切られており、functionFactory関数自体の名前が右側にあります。私にとって、私のバージョンははるかに論理的で明確であるように見えます。

int (*func)(int, int); 

この構文を模倣する機能がCで宣言されている方法構文:

+1

Cは、宣言子にインフィックス表記を使用します。これは「宣言は使用に従う」原則に従う。配列に 'a [5]'としてアクセスするので、int [5] a;ではなくint a [5];として宣言されます。関数型でも同じことが起こっています。 –

+0

'int(* function)(int、int)functionFactory(int n){...' "*ここでは戻り値の型(関数へのポインタ)*"戻り値の型ではない場合は、行? – TessellatingHeckler

+0

もちろん、これらの関数ポインタのtypedefを宣言することができます。そして、これらのtypedefを使った関数宣言は、より "正常"に見えます。 – paddy

答えて

1

次のコードは、funcという変数がintを返し、パラメータなどの2つのint Sを取る関数へのポインタであることを宣言する。

int func(int, int); 

これはどのように似ていますか?実際、C言語の関数は関数を指す変数と似ています。このサンプルを参照してください。

int funcA(int a, int b) { ... } 
int (*funcB)(int, int) = funcA; 

あなたがfuncA(a,b)funcB(a,b)を呼び出す場合には、コードの残りの部分は何の役割も果たしていない、それはありませんか?したがって、funcBが変数であり、funcA関数であっても、実際にはメモリのどこかに関数があり、funcAfuncBは両方とも関数コードのアドレスへのポインタです。

あなたがnitpickしたい場合は、funcAが一定であるとfuncBが可変であるので、funcAへの呼び出しのためのコンパイラの出力がfuncB用とは異なります。 1つのケースでは、funcBの宛先が時間とともに変化する可能性があるため、直接コールであり、場合によっては間接コールでもあります。これはアスタリスクが言っていることでもあります。「私は関数ではなく、私は関数へのポインタです。

Cでは、アスタリスクは常にポインタ変数を示す変数名の隣にあることに注意してください。そうでなければ、代わりにその変数を書く方法はありますか?このような?

// Careful! Wrong! 
int (int, int) *func; 

あなたの見た目は読みやすいですか?それとも、好きかもしれない?

// Careful! Wrong! 
int()(int, int) *func; 

この構文もあまり明確ではなく、他の既存のC構文をまねしません。あなたが関数ポインタで多くのことを作業する場合、周りにそれを渡し、変数に格納し、あなたの関数ポインタのための独自のタイプを宣言する必要があります。これは、宣言子構文がどのように動作するかの外れ

#include <stdio.h> 

int sum (int a, int b) { 
    return (a + b); 
} 

typedef int (* MathFuncType)(int, int); 

MathFuncType getSumFunc () { 
    return &sum; 
} 

int main(void) { 
    MathFuncType func = getSumFunc(); 
    printf("%d\n", func(3, 8)); 
    return 0; 
} 

http://rextester.com/GEKR20461

2

を。それは代替の観点から考えるのを助けるかもしれない。ここではそれを見てする一つの方法です:

T f (); // f is a function returning T 
    | 
    v 
T (*p) (); // p is a pointer to a function returning T 
    | 
    v 
T (*q())(); // q is a function returning a pointer to a function returning T 

だから我々は関数宣言子f()で始まります。次に、fをポインタ(*p)に置き換えて、宣言子(*p)()を与えます。最後に、pを関数q()に置き換えて、宣言子(*q())()を与えます。

編集

あなたは毛深い宣言子を読むとき、人々は「スパイラルルール」について話を聞くことがあります。それは実際のルールよりもガイドラインの多くのですが、それは次の優先順位のルールから外れ:

T *a[N]; // a is an array of pointer to T 
T (*a)[N]; // a is a pointer to an array of T 
T *f();  // f is a function returning T 
T (*f)(); // f is a pointer to a function returning T 

[]()演算子は単項*よりも高い優先順位を持ってpostfixの、括弧のためので、必要性配列へのポインタを宣言しますまたは機能。我々はT (*q())();を読むときに、私たちは一番左の識別子qから開始し、外側に「スパイラル」:

+-----------+ 
    | +-------+ | 
    | | +---+ | |   
    | | | | | | 
    T (* q())() 
     | | | | | | 
     | | +-+ | | 
     | +-----+ |  
     +---------+ 

は、1枚ずつ、それを破壊:

q  -- q is a 
    q()  -- function returning 
    *q()  -- pointer to 
    (*q())() -- function returning 
T (*q())(); -- T 

あなたは、関数型の配列を宣言しないでき、配列型を返す関数を宣言することもできません。

T a[N](); // NOT ALLOWED 
T f()[N]; // NOT ALLOWED 

f関数と配列型の場合、ポインタが常に関与するため、スパイラルルールが適用されます。

関連する問題