25

次のように、関数へのポインターの配列を宣言する必要があります。

extern void function1(void);
extern void function2(void);
...

void (*MESSAGE_HANDLERS[])(void) = {
   function1,
   function2,
   ...
};

ただし、配列を定数として宣言する必要があります-配列内のデータとデータへのポインターの両方。残念ながら、const キーワードをどこに配置すればよいか思い出せません。

実際のポインター (この場合は MESSAGE_HANDLERS) は、配列として宣言されているため、既に一定であると想定しています。一方、配列内の関数ポインタは、示されているように宣言されている場合、実行時に変更できませんか?

4

5 に答える 5

59

そんな型の作り方を覚えるテクニックがあります。まず、ポインタを名前から読み始め、右から左に読みます。

助けなしでそれを宣言する方法は?

配列

T t[5];

5 T の配列です。T を関数型にするには、戻り値の型を左側に、パラメーターを右側に記述します。

void t[5](void);

void を返し、パラメータを取らない 5 つの関数の配列になります。しかし、関数自体を配列に詰め込むことはできません! それらはオブジェクトではありません。それらへのポインターのみが可能です。

どうですか

void * t[5](void);

return-type を void へのポインターに変更するだけなので、これはまだ間違っています。括弧を使用する必要があります。

void (*t[5])(void);

これは実際に機能します。t は、void を返し、パラメーターを取らない関数への 5 つのポインターの配列です

すごい!arras へのポインタの配列はどうですか? それはとても似ています。要素タイプは左側に表示され、寸法は右側に表示されます。ここでも、かっこが必要です。そうしないと、配列が整数ポインターの多次元配列になるためです。

int (*t[5])[3];

それでおしまい!3 つの intの配列への 5 つのポインターの配列。

関数はどうですか?

今学んだことは、関数についても当てはまります。パラメーターをとらず、void を返す別の関数へのポインターを返す int を取る関数を宣言しましょう。

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

上記と同じ理由で、再び括弧が必要です。これで、それを呼び出して、返された関数をもう一度呼び出すことができます。

f(10)();

関数へのポインタを返す 関数への別のポインタを返す

これはどうですか?

f(10)(true)(3.4);

? 言い換えれば、int を取り、double を取り void を返す関数へのポインタを返す bool を取る関数へのポインタを返す関数は、どのように見えるでしょうか? 答えは、それらをネストするだけです:

void (*(*f(int))(bool))(double);

あなたは無限にそうすることができます。実際、関数へのポインターと同じように、配列へのポインターを返すこともできます。

int (*(*f(int))(bool))[3];

これはint を取る関数で、関数へのポインタを返します bool を取る関数は、3 つの int の配列へのポインタを返します

constと何の関係があるのですか?

上記で、基本的な型からより複雑な型を構築する方法を説明したconstので、それらがどこに属しているかがわかっている場所に配置できます。考慮してください:

T c * c * c ... * c name;

T、最後に指すことになる基本的な型です。cconst または not const のいずれかを表します。例えば

int const * const * name;

定数 int への定数ポインターへの型ポインターを持つように name を宣言します。を変更することはできnameますが、変更することはできません*name

int const * const

どちらも**nameタイプではない

int const

これを上記の関数ポインタに適用してみましょう:

void (* const t[5])(void);

これは、実際には配列が定数ポインターを含むことを宣言します。したがって、配列を作成 (および初期化)した後、星の後に が表示されるため、ポインターは const になります。この場合、星の前constに a を付けることはできないことに注意してください。定数関数へのポインターがないためです。関数を const にすることはできません。意味がないからです。したがって、以下は無効です。const

void (const * t[5])(void);

結論

関数と配列を宣言する C++ と C の方法は、実際には少し混乱します。最初に理解する必要がありますが、理解すれば、それを使用して非常にコンパクトな関数宣言を記述できます。

于 2008-12-03T16:26:53.273 に答える
16

このような状況では、typedef関数のシグネチャに名前を付けるために a を実行すると、はるかに簡単になります。

typedef void MESSAGE_HANDLER(void);

それがあれば、次のようになります。

MESSAGE_HANDLER * const handlers[] = { function1, function2 };

配列定数の実際の内容を取得します。

編集: からポインター部分を削除しましたtypedef。これは本当に優れています (ライブと学習)。

于 2008-12-03T15:23:01.017 に答える
15

cdecl言います:

cdecl> explain void (* const foo[])(void)
declare foo as array of const pointer to function (void) returning void

それはあなたが必要とするものですか?

于 2008-12-03T15:25:11.293 に答える
1

VisualStudio 2008 では、次のようになります。

void (* const MESSAGE_HANDLERS[])(void) = {
   NULL,
   NULL
};

int main ()
{
    /* Gives error 
        '=' : left operand must be l-value
    */
    MESSAGE_HANDLERS = NULL;

    /* Gives error 
        l-value specifies const object
    */
    MESSAGE_HANDLERS[0] = NULL;
}
于 2008-12-03T15:24:53.087 に答える
1

これが「C」で機能するかどうかはわかりません。「C++」で動作します:

  • 最初に MESSAGE_HANDLERS をタイプとして定義します。

    typedef void (*MESSAGE_HANDLER)();

  • 次に、型定義を使用して、配列を定数として宣言します。

    MESSAGE_HANDLER const handlers[] = {function1, function2};

'C' で意味的に同じことができれtypedefば、それも機能するはずです。

于 2008-12-03T15:38:25.680 に答える