web-dev-qa-db-ja.com

ネストされた「for」ループの数に制限はありますか?

すべてに制限があるので、ネストされたforループの数に制限があるのか​​、メモリがある限り追加できます、Visual Studioコンパイラはそのようなプログラムを作成できますか?

もちろん、64個以上のネストされたforループはデバッグに便利ではありませんが、実行可能でしょうか?

private void TestForLoop()
{
  for (int a = 0; a < 4; a++)
  {
    for (int b = 0; b < 56; b++)
    {
      for (int c = 0; c < 196; c++)
      {
        //etc....
      }
    }
  }
}
79
Fred Smith

私はこれを投稿して手足に出かけていますが、答えは次のとおりだと思います:

550と575の間

visual Studio 2015のデフォルト設定で

ネストされたforループを生成する小さなプログラムを作成しました...

for (int i0=0; i0<10; i0++)
{
    for (int i1=0; i1<10; i1++)
    {
        ...
        ...
        for (int i573=0; i573<10; i573++)
        {
            for (int i574=0; i574<10; i574++)
            {
                Console.WriteLine(i574);
            }
        }
        ...
        ...
    }
}

500個のネストされたループの場合、プログラムをコンパイルできます。 575ループの場合、コンパイラは次のことを行います。

警告AD0001アナライザー「Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer」は、「System.InsufficientExecutionStackException」タイプの例外をスローし、「スタックを十分に実行しないとプログラムを安全に実行できません」というメッセージを表示しました。これは、呼び出しスタック上に関数が多すぎるか、スタック領域が多すぎるためにスタック上に関数があるために発生する可能性があります。

基になるコンパイラメッセージ

エラーCS8078:式が長すぎるか複雑でコンパイルできない

もちろん、これは純粋に仮説的な結果です。最も内側のループがConsole.WriteLineを使用すると、スタックサイズを超える前にネストされたループが少なくなる可能性があります。また、これは厳密な技術的制限ではない場合があります。つまり、エラーメッセージに記載されている「アナライザ」または(必要に応じて)結果の実行可能ファイルの最大スタックサイズを増やす隠し設定があるという意味です。ただし、答えのこの部分は、C#の詳細を知っている人に任されています。


更新

コメントの質問 への応答:

not forループで使用されている場合、ローカル変数をスタックに配置できるかどうか、および/または575 非ネスト単一関数内のforループ

どちらの場合も、答えは次のとおりです。はい、可能です。メソッドに575個の自動生成ステートメントを入力する場合

int i0=0;
Console.WriteLine(i0);
int i1=0;
Console.WriteLine(i1);
...
int i574=0;
Console.WriteLine(i574);

まだコンパイルできます。他のすべてが私を驚かせたでしょう。 int変数に必要なスタックサイズはわずか2.3 KBです。しかし、私は興味があり、さらに制限をテストするために、この数を増やしました。最終的に、notコンパイルし、エラーが発生しました

エラーCS0204:コンパイラーによって生成されたものを含む65534ローカルのみが許可されます

これは興味深い点ですが、他の場所ですでに観察されています: メソッド内の変数の最大数

同様に、575 non-nestedfor- loops、次のように

for (int i0=0; i0<10; i0++)
{
    Console.WriteLine(i0);
}
for (int i1=0; i1<10; i1++)
{
    Console.WriteLine(i1);
}
...
for (int i574=0; i574<10; i574++)
{
    Console.WriteLine(i574);
}

コンパイルすることもできます。ここで、制限を見つけようとして、これらのループをさらに作成しました。特に、この場合のループ変数もals "locals"をカウントするかどうかはわかりません。なぜなら、それらは独自の{ block }。それでも、65534を超えることはできません。最後に、パターンの40000ループで構成されるテストを追加しました

for (int i39999 = 0; i39999 < 10; i39999++)
{
    int j = 0;
    Console.WriteLine(j + i39999);
}

これには追加の変数inループが含まれていましたが、これらは「ローカル」としてもカウントされるようで、これをコンパイルすることはできませんでした。


要約すると:〜550の制限は、ループのネストの深さによって引き起こされます。これは、エラーメッセージでも示されました。

エラーCS8078:式が長すぎるか複雑でコンパイルできない

エラーCS1647のドキュメント 残念ながら(しかし理解できるように)複雑さの「測定」を指定せず、実用的なアドバイスのみを提供します

コードを処理するコンパイラでスタックオーバーフローが発生しました。このエラーを解決するには、コードを簡素化します。

これを再度強調するために:深くネストされたfor- loopsの特定のケースでは、これはすべてかなりアカデミックで仮説的です。しかし、CS1647のエラーメッセージをウェブ検索すると、意図的に複雑ではない可能性が高いコードでこのエラーが表示されたいくつかのケースが明らかになりましたが、現実的なシナリオで作成されました。

119
Marco13

C#言語の仕様やCLRに強い制限はありません。コードは再帰的ではなく反復的であるため、スタックオーバーフローが非常に高速になります。

しきい値としてカウントできるものがいくつかあります。たとえば、(一般に)使用するintカウンターは、各ループのintをメモリに割り当てます(割り当て前に)あなたのスタック全体をintで...)。そのintの使用が必要であり、同じ変数を再利用できることに注意してください。

Marcoが指摘 のように、現在のしきい値は、実際の言語仕様やランタイムよりもコンパイラの方が多くなっています。それが再コーディングされると、さらに多くの反復が発生する可能性があります。 たとえばIdeoneを使用する場合 は、デフォルトで古いコンパイラを使用するため、1200を超えるforループを簡単に取得できます。

ただし、これほど多くのforループは、デザインが悪いことを示しています。この質問が純粋に仮説であることを願っています。

40
Patrick Hofman

MSILにコンパイルされるすべてのC#には制限があります。 MSILは65535個のローカル変数のみをサポートできます。 forループが例で示したループのようである場合、各ループには変数が必要です。

この制限を回避するために、コンパイラがヒープにオブジェクトを割り当ててローカル変数のストレージとして機能する可能性があります。しかし、そこからどのような奇妙な結果がもたらされるかはわかりません。そのようなアプローチを違法にする問題について、リフレクションで生じる可能性があります。

13
Cort Ammon

空のfor(;;)ループの場合は800と900の間

試行されたfor(;;)ループを除き、Marco13のアプローチをミラーリングしました。

_for (;;)  // 0
for (;;)  // 1
for (;;)  // 2
// ...
for (;;)  // n_max
{
  // empty body
}
_

ネストされた800個のfor(;;)で機能しましたが、900個のループを試行したときにMarco13が検出したのと同じエラーが発生しました。

コンパイルすると、for(;;)はCPUを最大限に使用せずにスレッドをブロックするように見えます。表面的には、Thread.Sleep()のように動作するようです。

7
Nat