web-dev-qa-db-ja.com

関数のポインターと関数のアドレス

したがって、関数ポインターを作成するときに、初期関数のアドレスを取得するためにoperator &は必要ないと考えました。

#include <stdio.h>

double foo (double x){
    return x*x;
}

int main () {

    double (*fun1)(double) = &foo;
    double (*fun2)(double) =  foo;

    printf("%f\n",fun1(10));
    printf("%f\n",fun2(10));

    printf("fun1 = %p \t &foo = %p\n",fun1, &foo);
    printf("fun2 = %p \t  foo = %p\n",fun2,  foo);       

    int a[10];

    printf("  a = %p \n &a = %p  \n",a,&a);

    return 0;
}

出力:

>./a.out 
100.000000
100.000000
fun1 = 0x4004f4      &foo = 0x4004f4
fun2 = 0x4004f4       foo = 0x4004f4
  a = 0x7fff26804470 
 &a = 0x7fff26804470 

そして、これは配列にも当てはまります。つまり、int a[10]が存在する場合、a&aの両方が同じ場所を指していることになります。なぜ配列と関数を使用するのですか?アドレスは、保存されている値(アドレス)と同じアドレスを持つメモリ位置に保存されていますか?

47
GradGuy

_int a[10]_を指定すると、aと_&a_の両方が同じアドレスを生成します(はい)が、タイプは異なります。

aは、タイプ_int[10]_です。暗黙的にポインター型に変換される場合、ポインターは_int*_型であり、配列の初期要素を指します。 _&a_はint (*)[10]型(つまり、10個の整数の配列へのポインター)です。配列にはパディングがないため、両方とも同じvalueのポインターを生成しますが、ポインターは異なりますtypes

関数は配列に似ていますが、完全に同じではありません。関数foodouble(double)型です。 fooが式で使用され、単項_&_演算子のオペランドではない場合は常に、double(*)(double)型のそれ自体へのポインターに暗黙的に変換されます。

したがって、すべての実用的な目的のために、関数の名前と同じ関数へのポインターは交換可能です。 「なぜこれらすべてのクレイジー関数ポインター定義はすべて機能するのですか?実際に何が起こっているのですか?」 (その質問が尋ねられましたC++についてですが、C++の非メンバー関数の規則はCの関数の規則と同じです。)

66
James McNellis

いいえ、関数/配列を指す専用の追加のストレージはありません。

ほとんどの変数では、variable_nameにはその変数のアドレスを取得する以外の意味があるため、&variableを使用してアドレスを取得する必要があります。

関数または配列の場合、function_name(それ自体、括弧は続きません)には他の意味がないため、関数のアドレスを取得するものとして解釈しても問題はありませんでした。

同様に逆に、通常のポインターは明示的に逆参照する必要がありますが、関数へのポインターは(他の妥当な解釈がないため)必要がないため、次のような関数へのポインターが与えられます:

int (*func)(param_list);

以下は互いに同等です-どちらもfuncが指す関数を呼び出します:

(*func)(params);

func(params);
8
Jerry Coffin

基本的に、関数名は関数として「既知」であるため、&は厳密には必要ありません。この動作は配列でも同じです。関数自体は変数ではないことを思い出してください。そのため、動作は時々予想とは少し異なります。 K&Rの第2版をお持ちの場合は、機能へのポインターに関するセクション5.11、または最後のリファレンスマニュアルをご覧ください。

セクションA7.1ポインタ生成:式または部分式の型が、ある型Tの「Tの配列」である場合、式の値は配列の最初のオブジェクトへのポインタであり、式の型は「Tへのポインター」に変更されました。この変換は、単項&演算子のオペランドである式の代わりに行われません...同様に、&演算子のオペランドとして使用される場合を除いて、「Tを返す関数」型の式は、「へのポインター」に変換されますTを返す関数。」

セクションA7.4.2アドレス演算子:単項&演算子は、オペランドのアドレスを取得します。結果は、左辺値によって参照されるオブジェクトまたは関数へのポインタです。オペランドのタイプがTの場合、結果のタイプは「Tへのポインター」です。

私の知る限り、これはC99でも同じです。

1
Cameron

funと_&fun_はまったく同じです(sizeof(f)が違法であることを除く)。 aと_&a_は、ポインター演算までは同じです:_a + 10 == &a + 1_、なぜなら10*sizeof(*a) == sizeof(a)(where sizeof(*a) == sizeof(int))のため。

1
Loic

printf( "fun1 =%p\t&foo =%p\n"、fun1、foo);

ここでは、pass by valueでFunction Pointerを渡してfooを呼び出しています。

printf( "fun2 =%p\t foo =%p\n"、fun2、&foo)

ここでは、&fooで関数ポインタを渡すことにより、pass by referenceを呼び出しています。

どちらの場合も、関数ポインターのみでprintfを呼び出しています。

foo自体はfunction pointer valueであり、 `変数ではないことに注意してください。

配列でも同じことが起こります。 int arr[10]は、10個の整数の連続ブロックの取得に変換され、最初の要素のアドレスがarrに格納されます。 arrはポインタでもあります。

0
rahul maindargi