関数ポインターの定義がアンパサンド「&」またはアスタリスク「*」をいくつ使用しても機能するのはなぜですか?


216

なぜ次のように機能するのですか?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}

回答:


224

これには、これらの演算子のすべての組み合わせが同じように機能するためのいくつかの要素があります。

これらすべてが機能する根本的な理由は、関数(などfoo)が関数へのポインターに暗黙的に変換可能であることです。これが機能する理由void (*p1_foo)() = foo;です。 fooは暗黙的にそれ自体へのポインタに変換され、そのポインタがに割り当てられp1_fooます。

unary &は、関数に適用されると、オブジェクトに適用されたときにオブジェクトのアドレスを生成するのと同じように、関数へのポインターを生成します。通常の関数へのポインターの場合、関数から関数へのポインターの暗黙的な変換のため、これは常に冗長です。いずれにせよ、これが機能する理由void (*p3_foo)() = &foo;です。

単項を*関数ポインタに適用すると、オブジェクトへの通常のポインタに適用されたときに、ポイントされたオブジェクトが生成されるのと同じように、ポイントされた関数が生成されます。

これらのルールは組み合わせることができます。最後から2番目の例を考えてみます**foo

  • 1つ目fooは暗黙的にそれ自体へのポインターに変換され、1つ目*はその関数ポインターに適用され、関数がfoo再び生成されます。
  • 次に、結果は再び暗黙的にそれ自体へのポインターに変換され、2番目*が適用されて、再び関数が生成されますfoo
  • 次に、暗黙的に再度関数ポインターに変換され、変数に割り当てられます。

はいくつでも追加でき*ます。結果は常に同じです。*sが多ければ多いほど、楽しいものになります。

5番目の例も考えられます&*foo

  • まず、foo暗黙的にそれ自体へのポインタに変換されます。単項*が適用され、foo再び降伏します。
  • 次に、&がに適用され、変数に割り当てられているへのfooポインタが生成fooされます。

&のみならず、その結果、ポインタツーpointer-である場合には、当然のことながら、関数ポインタが可変である、ない限り、関数ポインタ(に変換された関数に、ものの関数に適用することができますto-a-function;たとえば、リストに追加できますvoid (**pp_foo)() = &p7_foo;)。

これが機能&&fooしない理由です: &fooは関数ではありません。これは、右辺値である関数ポインターです。しかし、&*&*&*&*&*&*foo同じように、仕事と&******&fooそれらの式の両方であるため、&常に関数にしていない右辺値関数ポインタに適用されます。

また*、関数ポインタを介して呼び出すために単項を使用する必要がないことにも注意してください。両方(*p1_foo)();(p1_foo)();なぜなら関数に関数ポインタ変換再度、同一の結果を有します。


2
@Jimmy:これらは関数ポインタへの参照ではなく、単なる関数ポインタです。 &fooのアドレスを受け取りますfoo。これにより、関数ポインタがを指すfooようになります。
Dennis Zickefoose 2011

2
&オブジェクトの演算子をチェーンにすることもできません。指定さint p;れた&pは、ポインタを生成しp、右辺値式です。&オペレータは、左辺値式が必要です。
James McNellis、2011

12
同意しません。が多ければ多いほど*陽気さ少なくなります。
セスカーネギー

28
私の例の構文を編集しないでください。私は、言語の機能を示すために非常に具体的な例を選びました。
James McNellis、2012年

7
&*ちなみに、C標準では、相互にキャンセルする組み合わせ(6.5.3.2):"The unary & operator yields the address of its operand."/-/を 明示的に規定してい"If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue."ます。
ランディン2015年

9

Cは基礎となるマシンの単なる抽象化であり、これはその抽象化がリークしている場所の1つであることを覚えておくことも役立つと思います。

コンピュータから見ると、関数は単なるメモリアドレスであり、実行されると他の命令を実行します。したがって、Cの関数自体はアドレスとしてモデル化されます。これは、関数が指すアドレスと「同じ」であるという設計につながる可能性があります。


0

&そして *シンボル上の冪等の操作はC関数として宣言された手段func == *func == &func == *&funcと、したがって*func == **func

これは、タイプがあることを意味int ()と同じであるint (*)()関数パラメータとして渡すことができ定義FUNCとして*funcfuncまたは&func(&func)()と同じfunc()です。Godboltリンク。

関数は実際にはアドレスであり*&意味がなく、エラーを生成する代わりに、コンパイラはそれをfuncのアドレスとして解釈することを選択します。

&シンボルに(ただし、それは今別の目的を持っているので)、ポインタのアドレスを取得し、一方であろう関数ポインタとして宣言funcp*funcp同じであろう

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