5

以下のコードに似たものを書いていて、関数定義の本体内で誤って同じ関数を呼び出しました。

double function(double &value)
{
     //do something with a here
     if(some condition)
     {
           function(a);
     }
     return a;
}

次のような形式を検討してください。

int function(int &m)   {
    m = 2*m;
    if(m < 20)
    {
        function(m);
    }
    return m;
};

int main()  {
    int a = 2;
    std::cout <<"Now a = "<<function(a);
    return 1;
}

私によると、これはコンパイルどころか実行すべきではありません。しかし、それは実行され、正しい結果が得られます

今 a = 32

関数の定義を「終了」する前に関数を呼び出しました。それでも、それは機能します。なんで?

4

2 に答える 2

13

自分自身を呼び出す関数は、再帰関数として知られています。これが機能するのは、コンパイラが関数の定義ではなく関数の宣言のみを必要とするため、関数を呼び出すことができるからです。定義の最初の行は、宣言としても機能します。(詳細については、C++11 標準の§ 8.4.1.2を参照してください。)

再帰は、多くの問題を解決するのに適しています。再帰関数の典型的な例はfactorial関数です。と定義されていfactorial(n) = n * factorial(n-1)ます。

このコードを実行してみて、関数が自分自身を呼び出すときに何が起こるかをもう少し理解することができます。

#include <iostream>

int factorial(unsigned int n)
{
    std::cout << "Computing factorial of " << n << "\n";

    int result;
    if (n == 0) {
        result = 1;
    } else {
        result = n * factorial(n-1);
    }

    std::cout << "factorial(" << n << ") = " << result << "\n";
    return result;
}

int main()
{
    factorial(5);
}

宣言と定義の詳細については、この回答を参照してください。One Definition Ruleに関するウィキペディアのページも参考になるかもしれません。

于 2013-06-30T06:01:32.823 に答える
3

関数の「定義」の意味を誤解しているからです。

まず、セマンティクスを正しく理解しましょう。関数を参照するには、関数の完全な実装ではなく、関数の宣言だけが必要です。

通常、実行可能ファイルの作成には、1) コンパイルと 2) リンクの 2 つの手順があります。

コンパイル中

コンパイルのステップでは、各部分の構文が各関数で問題ないことがわかります。コンパイルするには、コア言語のキーワードまたは適切に宣言されたシンボルが必要です。コンパイラはこれらをまとめますが、これらのシンボルが実際に実行可能ファイル内の実際のオブジェクトを指しているかどうかは解決しません。オブジェクトは、データのビットまたは命令のビットのいずれかです。通常、これらのブロブは、オブジェクト ファイル内でデータ セグメントおよびテキスト セグメントと呼ばれます。

リンキング

コード内のすべての参照に対応するブロブがあるかどうかを確認するために、シンボルを使用してまだ識別されている各ブロブがまとめられるのは、リンクのステップです。

自己参照

構文が正しいかどうかを評価するために必要なのは宣言だけであることがわかりました (コンパイル手順)。関数自体の本体内にシンボル参照があるという事実は、まったく問題ではありません。

上記で行っていることは、次の省略形です。

int function(int &m); // implicit declaration 

int function(int &m) {
    m = 2*m;
    if(m < 20)
    {
        function(m); // this only requires the declaration to be accepted 
                     // by the compiler. 
    }
    return m;
};

int main()  {
    int a = 2;
    std::cout <<"Now a = "<<function(a);
    return 1;
}

この機能は、再帰関数を作成するための基本であるため、非常に重要です。一部の問題は、反復よりも再帰の方がはるかに洗練された方法で解決できます。特に、n 方向の再帰関数を投入し始める場合はそうです。

于 2013-06-30T06:22:27.210 に答える