중첩 된 'for'루프의 수에 제한이 있습니까?


79

모든 것이 제한이 있기 때문에 중첩 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....
      }
    }
  }
}

78
당신은 컴퓨터 프로그래머입니다. 컴퓨터 프로그래밍으로 자신의 질문에 답하십시오. 1, 2, 4, 8, 16, 32, ... 중첩 루프로 프로그램을 생성, 컴파일 및 실행하는 프로그램을 작성하면 제한이 있는지 매우 빠르게 배울 수 있습니다.
Eric Lippert

38
#1. OP는 이것이 가상의 "what-if"유형의 질문과는 달리 어떤 식 으로든 프로덕션 코드와 관련이 있음을 나타내지 않았습니다. # 2. 실험하는 동안 OP가 얼마나 많은 중첩 루프를 생성하더라도 무한대에 도달하지 않습니다. OP가 아무리 밀어 붙이더라도 한계가 있는지 여부를 증명할 수있는 유일한 방법은 실제로 중단되는 경우와시기입니다.
Panzercrisis

10
"모든 것이 한계가 있기 때문에", 아니오 : sin (x)에는 x-> oo에 대한 한계가 없습니다. 또한 : 만약 당신의 전제가 "모든 것이 제한적"이라면, 왜 "이것이 제한적입니까?" 대답은 주장을 완전히 쓸모없고 사소하게 만드는 당신의 가정에 있습니다.
Bakuriu

5
@EricLippert 엄밀히 말하면이 길을 얼마나 오래 계속해도 제한이 없는지 절대 확신 할 수 없습니다.
Casey

2
"x-> oo"가 나를 조금 울게 만들었습니다. :) "x → ∞"사용
Manu

답변:


120

나는 이것을 게시하여 팔다리로 나가고 있지만 대답은 다음과 같다고 생각합니다.

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 #을 깊이 알고있는 사람들에게 맡겨져 있습니다.


최신 정보

댓글질문에 대한 답변 :

이 답변이 확장 되어 for 루프에서 사용 되지 않는 경우 스택에 575 개의 로컬 변수를 넣을 수 있는지 여부 및 / 또는 575 개의 비 중첩 for 루프를 넣을 수 있는지 여부를 실험적으로 "증명"하는 것을보고 싶습니다 . 단일 기능

두 경우 모두 대답은 다음과 같습니다. 예, 가능합니다. 575 개의 자동 생성 문으로 메서드를 채울 때

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

여전히 컴파일 할 수 있습니다. 다른 모든 것이 나를 놀라게 할 것입니다. int변수에 필요한 스택 크기는 2.3KB에 불과합니다. 하지만 궁금해서 더 많은 한계를 테스트하기 위해이 숫자를 늘 렸습니다. 결국 컴파일 되지 않아 오류가 발생했습니다.

오류 CS0204 : 컴파일러에 의해 생성 된 로컬을 포함하여 65534 로컬 만 허용됩니다.

흥미로운 점이지만 다른 곳에서 이미 관찰 된 바 있습니다. 방법의 최대 변수 수

마찬가지로 575 개의 비 중첩 for 루프

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);
}

컴파일 할 수도 있습니다. 여기서도 한계를 찾으려고 노력했고 이러한 루프를 더 많이 만들었습니다. 특히,이 경우 루프 변수가 자체적으로 존재하기 때문에 "locals"도 계산하는지 여부를 확신 할 수 없었습니다 { block }. 그러나 여전히 65534 이상은 불가능합니다. 마지막으로 40000 개의 패턴 루프로 구성된 테스트를 추가했습니다.

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

즉, 추가 변수 포함 에서 루프를하지만, 이러한뿐만 아니라 "지역 주민"으로 간주하는 것, 그리고이 컴파일 할 수 없었습니다.


요약하자면 ~ 550의 한계는 실제로 루프 의 중첩 깊이 로 인해 발생합니다 . 이것은 또한 오류 메시지로 표시되었습니다.

오류 CS8078 :식이 너무 길거나 복잡하여 컴파일 할 수 없습니다.

오류 CS1647에 대한 문서는 불행히도 (이해할 만하게도 ) 복잡성의 "측정"을 지정하지 않고 실용적인 조언 만 제공합니다.

코드를 처리하는 컴파일러에 스택 오버플로가 있습니다. 이 오류를 해결하려면 코드를 단순화하십시오.

이것을 다시 강조하기 위해 : 깊게 중첩 된 for루프 의 특별한 경우 에이 모든 것은 다소 학문적이고 가설 적 입니다. 그러나 CS1647의 오류 메시지에 대한 웹 검색을 통해이 오류가 의도적으로 복잡하지는 않지만 실제 시나리오에서 생성 된 코드에 대해 나타나는 몇 가지 경우를 알 수 있습니다.


2
@PatrickHofman 예, "기본 설정 포함 ..." 이라는 힌트 가 중요합니다. 이것은 VS2015에서 기본 프로젝트를 순진하게 만드는 것을 의미합니다. 프로젝트 설정은 한계를 높이기 위해 "명백한"매개 변수를 표시하지 않았습니다. 그러나 그와 상관없이 CLI / CLR / 무엇이든 더 이상 제한이 없는지 궁금합니다. ECMA-335 사양이 섹션이있다 "MAXSTACK를 제공해야 III.1.7.4를" ,하지만이 정말 관련이있다, 또는 구체적으로 그 의미를 분석 할 것인지 말 너무 많은 C #을 멍청한 놈의이야 ....
Marco13 dec.

10
마이크로 소프트가 for루프에 제한을 둔 것은 이번이 처음은 아닐 것이다 . Microsoft BASIC의 중첩 제한은 8입니다.
Mark

3
@Davislor : 오류 메시지는 컴파일러 의 스택 크기를 나타냅니다 . 이는 프로그램이기도하며 중첩 루프 구조를 재귀 적으로 처리합니다. 컴파일중인 코드의 지역 변수 수와 밀접하게 관련되어 있지 않습니다.
ruakh

2
나 뿐인가요? 나는이 대답이 완전히 재밌다고 생각합니다! 나는 또한 42대답으로 생각 했을 것입니다.
cmroanirgo

11
루프 당 2 번의 반복과 사이클 당 1 번의 반복 만 가정하면 500 개의 중첩 루프가 4GHz 컴퓨터에서 실행되는 데 약 10 ^ 33 구골 년 (10 ^ 133)이 걸린다는 점에 주목할 가치가 있습니다. 우주의 나이를 비교하는 것은 약 1.37 x 10 ^ 10 년입니다. 핵이 약 10 ^ 40 년 후에 붕괴 될 것으로 예상된다는 점을 감안할 때 현재 하드웨어는 그렇게 오래 지속되지 않으며 그 동안 컴퓨터를 다시 시작해야 할 수도 있습니다.
Maciej Piechotka

40

C # 언어 사양 또는 CLR에는 엄격한 제한이 없습니다. 코드는 재귀 적이기보다는 반복적이기 때문에 스택 오버플로가 매우 빠르게 발생할 수 있습니다.

임계 값으로 계산할 수있는 몇 가지 항목이 있습니다. 예를 들어 (일반적으로) 사용하는 int카운터 int는 각 루프에 대해 메모리 내를 할당합니다 (그리고 int로 전체 스택을 할당하기 전에 ...). 이를 사용해야 int하며 동일한 변수를 재사용 할 수 있습니다.

마르코 지적 , 현재의 임계 값은 실제 언어 사양 또는 런타임에 비해 컴파일러에서 더 많은 것이다. 다시 코딩되면 훨씬 더 많은 반복 작업을 수행 할 수 있습니다. 예를 들어 기본적으로 이전 컴파일러를 사용하는 Ideone을 사용하면 1200 개 이상의 for루프를 쉽게 얻을 수 있습니다.

하지만 많은 for 루프는 잘못된 디자인의 지표입니다. 이 질문이 순전히 가설 적이기를 바랍니다.


나는 이것에 동의하지만 소프트웨어 제한에 도달하기 전에 시간 / 하드웨어 제한에 도달 할 가능성이 있다고 덧붙입니다 (예 : 컴파일하는 데 너무 오래 걸리거나 코드를 실행하는 데 너무 오래 / 너무 많은 리소스가 소요됨).
Marshall Tigerus

백만 줄의 코드 파일은 매우 빠르게 컴파일되므로 문제가되지 않습니다. 또한 코드는 실행 엔진에 매우 간단하기 때문에 성능 측면에서 그다지 기대하지 않습니다.
Patrick Hofman

1
어서, 언어 자체에는 제한이 없습니다. 파일 시스템은가 20MB 소스 파일이나 너무 큰지고 실행은 Marco13 @ 진정한 제한되지 않는 것을
패트릭 호프만

3
귀하의 답변과 달리 각 루프는 실제로 사용되는 스택 공간에 추가됩니다. (새 변수를 추가하는 것입니다.) 따라서 변수가 충분하면 스택이 소진됩니다. 재귀와 똑같은 문제입니다 (메서드의 스택 프레임이 하나보다 더 많은 스택 공간을 차지한다는 점을 제외하면 int). 루프 변수가없는 루프라면 다를 수 있습니다.
Servy dec

3
CPython은 언어 구조의 중첩을 약 50 개로 제한한다고 생각합니다. 1200 개의 중첩 된 루프도 지원할 필요가 없습니다. 이미 10 개는 프로그램에 문제가 있다는 명확한 신호입니다.
Bakuriu

13

MSIL로 컴파일 된 모든 C #에는 제한이 있습니다. MSIL은 65535 지역 변수 만 지원할 수 있습니다. for루프가 예제에서 보여준 것과 같으면 각 루프에는 변수가 필요합니다.

컴파일러가 힙에 개체를 할당하여 로컬 변수의 저장소 역할을하여이 제한을 피할 수 있습니다. 그러나 나는 그것에서 어떤 종류의 이상한 결과가 나올지 잘 모르겠습니다. 그러한 접근을 불법으로 만드는 반성에서 발생하는 문제가있을 수 있습니다.


6
for 루프에는 변수가 필요하지 않습니다.
Patrick Hofman

1
@PatrickHofman 당신이 맞아요, 내가 포함하는 것을 잊은 부분을 편집했습니다
Cort Ammon

@PatrickHofman 변수가없는 for 루프 while(true)는 무한 루프를 형성하는 것과 동일합니다 . 질문을 "종료 프로그램에서 중첩 된 for 루프 수에 제한이 있습니까?"로 편집 한 경우 그런 다음 로컬 변수 트릭을 사용하여 하드 제한을 형성 할 수 있습니까?
모리

2
@emory 변수를 재사용하는 정말 터무니없는 루프에서 멈추는 것은 없습니다. 나는 그것들을 의미있는 for 루프라고 부르지는 않을 것이지만, 그렇게 할 수 있습니다.
Cort Ammon

1
@emory 당신은 함께 루프를 종료 할 수 break또는 같은 외부 루프 조건 체크 뭔가가( ; DateTime.now() < ... ; )
Grisha Levit 보낸

7

for(;;)루프의 경우 800에서 900 사이 .

시도 된 for(;;)루프를 제외하고 미러링 된 Marco13의 접근 방식 :

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

800 nested for(;;)에서 작동했지만 Marco13이 900 루프를 시도 할 때 발생한 것과 동일한 오류가 발생했습니다.

컴파일 할 때 for(;;)CPU를 최대로 사용하지 않고 스레드를 차단하는 것처럼 보입니다. 표면적으로는 Thread.Sleep().


2
"그것은 거의 즉시 실행을 완료합니다" -이상합니다 for(;;)-C #에서 무한 루프가 될 것이라고 생각했습니다 ! (지금은 그것을 밖으로 시도 할 수는 없지만 잘못된 것으로 밝혀지면, 나는이 댓글이 삭제됩니다)
Marco13

@ Marco13 : 네, 맞아요! 이전에 테스트 코드에서 무슨 일이 있었는지 확실하지 않지만 for(;;)CPU 시간을 소비하지 않고 차단됩니다.

for (;;) 루프는 무한 루프이므로 다른 for (;;) 루프를 추가하거나 실행되지 않기 때문에 800 개를 추가해야합니다. 첫 번째는 영원히 실행됩니다.
Fred Smith

1
@FredSmith : for(;;)루프는 중첩되어 있으므로 모두 무한 중첩 루프까지 시작합니다. 처음 for(;;)차단하려면 for(;;){}. 이렇게하는 이유는 현재 .NET / C # 구현에 어떤 제한이 있는지 확인하는 테스트였습니다. 분명히 그것은 900 for(;;)도까지 갈 수 없지만 , 아시다시피 아무것도하지 않습니다.
Nat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.