web-dev-qa-db-ja.com

「typedefint(f)(void)」のような括弧付きのtypedefはどういう意味ですか?関数プロトタイプですか?

typedef int (fc_name) (void);

ここに fc_nameは任意の有効なC記号です。

これは関数ポインタtypedefとどのように異なりますか?

29
Hemanth

これは、関数型に対するtypedefです。目的は関数ポインタに使用することですが、この場合、使用する構文は次のようになります。

int bar(void);

fc_name* foo = bar; /* Note the * */

更新: Jonathan Lefflerの回答 へのコメントで述べたように、typedefを使用して関数を宣言できます。 1つの用途は、コールバック関数のセットを宣言することです。

typedef int (callback)(int, void*);

callback onFoo;
callback onBar;
callback onBaz;
callback onQux;
34
jamesdlin

最初の括弧は不要です-これは次と同等です:

_typedef int fc_name(void);
_

GCCにそれ自体で文句を言わせることはできませんが、これは何の役にも立たないと思います。

これは、_fc_name_が、引数をとらずにintを返す関数型のエイリアスであることを意味します。たとえば、次のコマンドを使用してRand()関数を宣言することはできますが、それほど便利ではありません。

_fc_name Rand;
_

関数定義でtypedefを使用することはできません。

関数typedefへのポインタは次のようになります。

_typedef int (*fc_name)(void);
_

このコードは、アスタリスクのないtypedefが関数ポインターではないことを示しています(現在削除されている代替回答に対応しています)。

_static int function(void)
{
    return 0;
}

typedef int   fc_name1 (void);
typedef int  (fc_name2)(void);
typedef int (*fc_name3)(void);

fc_name1 x = function;
fc_name2 y = function;
fc_name3 z = function;
_

コンパイルすると、「gcc」は次のように言います。

_gcc -Wextra -Wall -pedantic -c -O x.c
x.c:10:1: error: function ‘x’ is initialized like a variable
x.c:11:1: error: function ‘y’ is initialized like a variable
_

そして、このコードは、 jamesdlin :によって提案されているように、実際に_fc_name *var = funcname;_を使用できることを示しています。

_static int function(void)
{
    return 0;
}

typedef int   fc_name1 (void);
typedef int  (fc_name2)(void);
typedef int (*fc_name3)(void);

fc_name1  x_0 = function;
fc_name1 *x_1 = function;
fc_name2  y_0 = function;    // Damn Bessel functions - and no <math.h>
fc_name2 *y_1 = function;    // Damn Bessel functions - and no <math.h>
fc_name3  z   = function;
_

Y0を使用して、y1はGCC警告を生成します。

_x.c:12:11: warning: conflicting types for built-in function ‘y0’
x.c:13:11: warning: built-in function ‘y1’ declared as non-function
_

そして、 schot からのコメントに基づいて構築します:

_static int function(void)
{
    return 0;
}

typedef int   fc_name1 (void);
typedef int  (fc_name2)(void);
typedef int (*fc_name3)(void);

fc_name1  x_0 = function;   // Error
fc_name1 *x_1 = function;   // x_1 is a pointer to function
fc_name1  x_2;              // Declare int x_2(void);
fc_name1 *x_3 = x_2;        // Declare x_3 initialized with x_2

fc_name2  y_0 = function;   // Damn Bessel functions - and no <math.h>
fc_name2 *y_1 = function;   // Damn Bessel functions - and no <math.h>
fc_name1  y_2;              // Declare int y_2(void);
fc_name1 *y_3 = x_2;        // Declare y_3 initialized with y_2

fc_name3  z   = function;
_

興味深い-Cの暗いコーナーは確かに暗いです。

17

これがtypedef nameに対して行われるのを見たことがありませんが、functionの名前を括弧で囲むと、関数のようなマクロとして展開されるのを防ぐのに役立ちます。同名。たとえば、isxxx関数はctype.hは、関数とマクロの両方として定義されています。これは、isalphaへのポインタを取ることができるようにするためです。しかし、Cライブラリはどのようにdefineアウトオブラインisalpha?おそらくこのように:

#include <ctype.h>

int
(isalpha)(int c)
{
    return isalpha(c);
}

関数本体でのisalphaの使用はマクロとして拡張されますが、関数ヘッダーでの使用は拡張されません。

1
zwol

面白い! typedef宣言は、ストレージクラスとしてtypedefを使用する宣言です。

typedef int   fc_name1 (void);   
// this defines a function type called fc_name1 
// which takes no parameter and returns int

後で、次のような関数を定義できます。

fc_name1 myFunc;
// this is equivalent to the next line
// int myFunc(void);

これはc/c ++標準から理解できるはずです。

1
Daniel

正しい形式は次のとおりです。

typedef int (*myfunc)(void);

次のような関数を定義できます。

int helloword(void) {
    printf("hello, world\n");
}

次に、この関数への変数ポイントを定義します。

myfunc hw_func;
hw_func = helloworld;

関数ポインタで関数を呼び出します。

int ret = (*hw_func)();

関数ポインタが必要な理由は、C言語には事前定義された関数ポインタがなく、void *ポインタを使用して関数を呼び出すことはC言語では違法であるためです。

0
diabloneo
  1 #include <stdio.h>
  2 
  3 
  4 typedef int (fc_name)(void);
  5 
  6 
  7 
  8 int test_func1 ()
  9 {
 10     printf("\n test_func1 called\n");
 11 
 12     return 0;
 13 }
 14 
 15 int test_func2 (void)
 16 {
 17     printf("\n test_func2 called\n");
 18     return 0;
 19 }
 20 
 21 int handler_func(fc_name *fptr)
 22 {
 23     //Call the actual function
 24     fptr();
 25 }
 26 
 27 int main(void)
 28 {
 29     fc_name  *f1, *f2;
 30 
 31     f1 = test_func1;
 32     f2 = test_func2;
 33 
 34     handler_func(f1);
 35     handler_func(f2);
 36 
 37     printf("\n test complete\n");
 38 
 39     return 0;
 40 }

出力:-

 test_func1 called

 test_func2 called

 test complete

したがって、私が質問したtypedef(ここでは4行目)は関数型を表しており、関数ポインターtypedefと同じではありません。この種のtypedefはあまり重要ではありません。これらは、スタイルの標準として、または単に意図的に難読化を作成するために使用されます;-)

0
Hemanth