Typedef関数ポインター?


459

DLLを動的にロードする方法を学習していますが、理解できないのはこの行です

typedef void (*FunctionFunc)();

少し質問があります。誰かがそれに答えることができれば私は感謝しています。

  1. なぜtypedef使用されるのですか?
  2. 構文は奇妙に見えます。後にvoid関数名や何かがあるべきではありませんか?匿名関数のように見えます。
  3. 関数のメモリアドレスを格納するために関数ポインタが作成されていますか?

だから私は今混乱しています。わかりやすく説明してもらえますか?


5
リンクを見てください(最後のセクション)learncpp.com/cpp-tutorial/78-function-pointers
enthusiasticgeek

9
using FunctionFunc = void (*)();代わりにc ++ 11 を使用できることに注意してください。型(関数へのポインタ)の名前を宣言しているだけであることはもう少し明確です
user362515

ちょうど@ user362515に追加するには、私には少し明確な形式は次のとおりです。using FunctionFunc = void(void);
トップスピン

@topspin IIRCこれら2つは同じではありません。1つは関数ポインタ型で、もう1つは関数型です。暗黙の変換があります。それが機能する理由です。IANA(C ++)Lなので、介入して修正できます。いずれにせよ、ポインタ型を定義することを意図している場合は、構文を*より明確にする必要があると思います。
user362515 2016年

回答:


463

typedef名前を型に関連付ける言語構造です。
たとえば、元のタイプと同じように使用します。

  typedef int myinteger;
  typedef char *mystring;
  typedef void (*myfunc)();

のようにそれらを使用して

  myinteger i;   // is equivalent to    int i;
  mystring s;    // is the same as      char *s;
  myfunc f;      // compile equally as  void (*f)();

ご覧のとおり、typedefされた名前を上記の定義で置き換えることができます。

問題は、CおよびC ++での関数の構文と読みやすさへのポインターにあり、そのtypedefような宣言の読みやすさを向上させることができます。ただし、構文は適切です。関数-他のより単純な型とは異なり、戻り値とパラメーターがあり、関数へのポインターの長くて複雑な宣言が含まれる場合があるためです。

読みやすさは、関数配列へのポインタやその他のさらに間接的なフレーバーによって、非常にトリッキーになり始める場合があります。

3つの質問に答えるには

  • typedefが使用されるのはなぜですか? コードの読み取りを容易にするため-特に関数へのポインター、または構造体名の場合。

  • 構文は奇妙に見えます(関数宣言へのポインタで)。 その構文は、少なくとも最初は読みにくいです。typedef代わりに宣言を使用すると、読みやすくなります

  • 関数のメモリアドレスを格納するために関数ポインタが作成されていますか? はい、関数ポインタは関数のアドレスを格納します。これはtypedef、プログラムの書き込み/読み取りを容易にするだけの構造とは関係ありません。コンパイラは、実際のコードをコンパイルする前にtypedef定義を展開するだけです。

例:

typedef int (*t_somefunc)(int,int);

int product(int u, int v) {
  return u*v;
}

t_somefunc afunc = &product;
...
int x2 = (*afunc)(123, 456); // call product() to calculate 123*456

6
最後の例では、 'square'が同じことを指すだけではなく、&squareを使用する代わりに関数へのポインターを参照します。
pranavk 2013年

2
質問、最初のtypedefの例では、フォームtypedef type aliasがありますが、関数ポインターでは2つの引数しかありませんtypedef type。エイリアスのデフォルトは、type引数で指定された名前ですか?
dchhetri 2013年

2
@pranavk:はい、squareそして&square(そして実際、*squareそして**square)すべてが同じ関数ポインタを参照しています。
ジョナサンレフラー2013年

6
@ user814628:あなたが何を求めているのかは明確ではありません。ではtypedef int newnamenewnameのエイリアスにしていintます。typedef int (*func)(int)あなたが作っているfuncのエイリアスにint (*)(int)関数へのポインタを取る- int引数をと返すint値を。
ジョナサンレフラー、2013年

10
私は注文について混乱しているだけだと思います。typedef int (*func)(int)を使用すると、funcがエイリアスであることを理解しています。エイリアスがタイプに絡まっているため、少し混乱しています。行くtypedef int INTのtypedef関数ポインタの形式であった場合の例として、私がより容易になるであろうtypedef int(*function)(int) FUNC_1。そうすれば、1つにメッシュ化される代わりに、2つの別個のトークンでタイプとエイリアスを確認できます。
dchhetri 2013年

188
  1. typedefタイプのエイリアスに使用されます。この場合、のエイリアスにFunctionFuncなりvoid(*)()ます。

  2. 確かに構文は奇妙に見えますが、これを見てください:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
    
  3. いいえ、これは単にFunctionFunc型が関数ポインターになることをコンパイラーに通知するだけで、次のように定義されません。

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"
    

27
typedef新しい型を宣言しませ。あなたtypedefは同じタイプの多くの定義された名前を持つことができ、それらは区別されません(例えば、関数のオーバーロード)。名前の使用方法に関して、- typedef定義されたtypedef名前が定義された名前と完全に同じではない状況がいくつかありますが、同じものに対して複数の-定義された名前は同じです。
乾杯とhth。-Alf、

ああ、私は今それを手に入れました。ありがとう。
ジャックハービン2010年

4
質問2の回答の+1-非常に明確。残りの答えも明確でしたが、それは私にとって際立っています:-)
Michael van der Westhuizen 2012年

33

typedef単語がなければ、C ++では、宣言はFunctionFunc引数のない関数へのポインター型の変数を宣言し、を返しvoidます。

ではtypedefではなく定義しFunctionFunc、その型の名前として。


5
これはそれについて考える本当に良い方法です。他の回答を注意深く読んでも、エイリアスと名前のもつれに関するOPの混乱に実際には対応していません。この投稿を読んで新しいことを学びました。
Mad Physicist

9

C ++ 11を使用できる場合はstd::functionusingキーワードを使用することをお勧めします。

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;

1
C ++ 11なしのusingキーワードはのようになります。typedef std::function<void(int, std::string)> FunctionFunc;誰かがC ++ 11なしの関数の別のラッパーを必要とする場合に備えて
Top-Master

2
#include <stdio.h>
#include <math.h>

/*
To define a new type name with typedef, follow these steps:
1. Write the statement as if a variable of the desired type were being declared.
2. Where the name of the declared variable would normally appear, substitute the new type name.
3. In front of everything, place the keyword typedef.
*/

// typedef a primitive data type
typedef double distance;

// typedef struct 
typedef struct{
    int x;
    int y;
} point;

//typedef an array 
typedef point points[100]; 

points ps = {0}; // ps is an array of 100 point 

// typedef a function
typedef distance (*distanceFun_p)(point,point) ; // TYPE_DEF distanceFun_p TO BE int (*distanceFun_p)(point,point)

// prototype a function     
distance findDistance(point, point);

int main(int argc, char const *argv[])
{
    // delcare a function pointer 
    distanceFun_p func_p;

    // initialize the function pointer with a function address
    func_p = findDistance;

    // initialize two point variables 
    point p1 = {0,0} , p2 = {1,1};

    // call the function through the pointer
    distance d = func_p(p1,p2);

    printf("the distance is %f\n", d );

    return 0;
}

distance findDistance(point p1, point p2)
{
distance xdiff =  p1.x - p2.x;
distance ydiff =  p1.y - p2.y;

return sqrt( (xdiff * xdiff) + (ydiff * ydiff) );
}

1
「&」はどのような状況で必要ですか?たとえば、「func_p =&findDistance」と「func_p = findDistance」は同じようにうまく機能しますか?
ドナ、

1
関数名はポインタなので、 '&'を使用する必要はありません。言い換えると、関数ポインタが考慮される場合、「&」はオプションです。ただし、プリミティブデータタイプの場合、アドレスを取得するには「&」を使用する必要があります。
Amjad 2017年

1
「&」がオプションになる可能性があるというのはいつも奇妙なことです。typedefを使用してプリミティブへのポインターを作成する場合(つまりtypedef (*double) p、「&」はオプションですか?
Donna

typedef double* pdoubleへのポインターを定義するために入力したいと思います。あなたが移入したい場合はp、プリミティブ変数からポインタを、あなたは「&」を使用する必要があります。ちなみに、* <ポインタ名>は、ポインタを逆参照します。*ポインタ(関数名)を間接参照し、機能命令が置かれているメモリのブロックに移動する関数ポインタに使用されます。
Amjad 2017年

2

構文の一般的なケースについては、ANSI C規格の付録Aを参照してください。

そこからバッカスナウルフォームで、がtypedefタイプであることがわかりますstorage-class-specifier

タイプのdeclaration-specifiersあなたは、多くの指定子の種類を混在させることができていることがわかります、の順番は重要ではありません。

たとえば、次のように言うのは正しいことです。

long typedef long a;

タイプaをのエイリアスとして定義しますlong long。そのため、徹底的な使用に関するtypedefを理解するには、構文を定義するbackus-naurフォームを調べる必要があります(ISOだけでなく、ANSI Cには正しい文法がたくさんあります)。

typedefを使用して関数型のエイリアスを定義する場合、関数の識別子を配置するのと同じ場所にエイリアスを配置する必要があります。あなたのケースでFunctionFuncは、呼び出し時に型チェックが無効で何も返さない関数へのポインタのエイリアスとして型を定義します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.