20

関数名を関数ポインターとして使用することは、address-of演算子を関数名に適用することと同等であるというのは興味深いことです。

これが例です。

typedef bool (*FunType)(int);
bool f(int);
int main() {
  FunType a = f;
  FunType b = &a; // Sure, here's an error.
  FunType c = &f; // This is not an error, though. 
                  // It's equivalent to the statement without "&".
                  // So we have c equals a.
  return 0;
}

名前の使用は、配列ですでにわかっていることです。しかし、あなたは次のようなものを書くことはできません

int a[2];
int * b = &a; // Error!

言語の他の部分と一致していないようです。このデザインの理論的根拠は何ですか?

この質問では、そのような動作のセマンティクスと、それが機能する理由について説明します。しかし、なぜ言語がこのように設計されたのか興味があります。

さらに興味深いのは、関数型は、パラメーターとして使用する場合は暗黙的にそれ自体へのポインターに変換できますが、戻り型として使用する場合はそれ自体へのポインターに変換されないことです。

例:

typedef bool FunctionType(int);
void g(FunctionType); // Implicitly converted to void g(FunctionType *).
FunctionType h(); // Error!
FunctionType * j(); // Return a function pointer to a function 
                    // that has the type of bool(int).
4

3 に答える 3

20

この動作の理論的根拠を具体的に尋ねているので、これが私が見つけることができる最も近いものです(ANSI C90理論的根拠のドキュメントから-http ://www.lysator.liu.se/c/rat/c3.html#3-3- 2-2):

3.3.2.2関数呼び出し

(*pf)()関数へのポインタは、としてまたはとして使用できますpf()。後者の構成は、ベースドキュメントで認可されておらず、Cの現在のバージョンの一部に表示され、明確で、古いコードを無効にせず、重要な省略形になる可能性があります。省略形は、オブジェクトと関数へのポインタでいっぱいの構造を指定する外部名を1つだけ提示するパッケージに役立ちます。メンバー関数は、graphics.open(file)の代わりに 呼び出すことができます(*graphics.open)(file)。関数指定子の扱いは、いくつかの奇妙な、しかし有効な構文形式につながる可能性があります。宣言が与えられた:

int f ( ) , ( *pf ) ( ) ; 

その場合、次の式はすべて有効な関数呼び出しです。

( &f)(); f(); (*f)(); (**f)(); (***f)();
pf(); (*pf)(); (**pf)(); (***pf)();

各行の最初の式については、前の段落で説明しました。2つ目は従来の使用法です。以降のすべての式は、ほぼすべての式のコンテキストで、関数指定子のポインター値への暗黙的な変換を利用します。委員会は、これらのフォームを許可することに実際の害はないと考えました。のようなフォームを非合法化することは(*f)()許可しますが*a(のためint a[])に、それが価値があるよりも単に厄介なように見えました。

基本的に、関数ポインターと関数ポインターの同等性が追加され、関数ポインターの使用が少し便利になりました。

于 2012-08-28T03:43:42.353 に答える
13

これはCから継承された機能です。

Cでは、関数の名前自体が意味するものが他にほとんどないため、これが許可されます。実際の関数でできることは、それを呼び出すことだけです。あなたがそれを呼んでいないなら、あなたがすることができる唯一のことはアドレスを取ることです。あいまいさがないため、関数名の後に(関数の呼び出しを示すaが付いていない場合は常に、名前は関数のアドレスに評価されます。

&これは実際には言語の他の部分と多少似ています。配列の名前は、かなり限られた状況(またはのオペランドとして使用される)を除いて、配列の最初の要素のアドレスに評価されますsizeof

Cが許可したので、C ++も同様に機能します。これは、ほとんど同じことが当てはまるためです。関数で実行できるのは、関数を呼び出すか、アドレスを取得することだけです。したがって、名前の後に(関数呼び出しを示すaが付いていない場合は、次に、名前はあいまいさのないアドレスに評価されます。

于 2012-08-28T02:57:53.360 に答える
2

配列の場合、address-of演算子を使用してもポインターの減衰はありません。

int a[2];
int * p1 = a;      // No address-of operator, so type is int*
int (*p2)[2] = &a; // Address-of operator used, so type is int (*)[2]

配列とポインタは異なるタイプであり、たとえば、関数内で配列への参照を返したり、配列への参照を渡したりできるため、これは理にかなっています。

しかし、関数を使用すると、他にどのようなタイプが可能でしょうか?

void foo(){}
&foo; // #1
foo;  // #2

#2だけがタイプを与えると想像してみましょう、タイプvoid(*)()は何でしょう&fooか?他の可能性はありません。

于 2012-08-28T04:17:29.243 に答える