중첩 된 '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.