스위치에서 break를 사용해야하는 이유는 무엇입니까?


74

각 문마다 그 switch구성 (많은 언어로)을 사용 하기로 결정한 (그리고 어떤 개념에 기초하여) 누가 break?

왜 이런 식으로 작성해야합니까?

switch(a)
{
    case 1:
        result = 'one';
        break;
    case 2:
        result = 'two';
        break;
    default:
        result = 'not determined';
        break;
}

(PHP와 JS에서 이것을 주목했습니다; 아마도 이것을 사용하는 다른 많은 언어가있을 것입니다)

switch의 대안 인 경우 if와 동일한 구성을 사용할 수없는 이유는 if무엇입니까? 즉 :

switch(a)
{
    case 1:
    {
        result = 'one';
    }
    case 2:
    {
        result = 'two';
    }
    default:
    {
        result = 'not determined';
    }
}

break현재 블록 다음에 블록이 실행 되는 것을 방지 한다고합니다 . 그러나 누군가가 실제로 현재 블록을 실행하고 다음 블록을 실행할 필요가있는 상황에 처하게됩니까? 나는하지 않았다. 나를 위해, break항상 있습니다. 모든 블록에서. 모든 코드에서.


1
대부분의 답변에서 알 수 있듯이 이는 여러 조건이 동일한 'then'블록을 통해 라우팅되는 'fall-through'와 관련이 있습니다. 그러나 이는 언어에 따라 다릅니다. 예를 들어 RPG는 CASE명령문을 거대한 if / elseif 블록과 동일하게 취급합니다 .
Clockwork-Muse

34
C- 디자이너의 잘못된 결정이었습니다 (많은 결정과 마찬가지로 어셈블리-> C로 전환하고 프로그래밍을 쉽게하기보다는 변환을 더 쉽게하도록 결정됨) . 불행히도 상속되었습니다. 다른 C 기반 언어로. 간단한 규칙을 사용하여 "항상 일반적인 경우를 기본값으로 설정하십시오 !!" , 우리는 가장 일반적인 경우이기 때문에 별도의 키워드를 사용 하여 기본 동작 중단 되어야 함 을 알 수 있습니다 .
BlueRaja-대니 Pflughoeft

4
필자는 필자가 선호하는 언어 (C)로 된 스위치 문이이를 피하도록 설계되었지만 여러 차례에 걸쳐 실패를 사용했습니다. 예를 들어 나는 case 'a': case 'A': case 'b': case 'B'했지만 대부분 할 수 없기 때문이다 case in [ 'a', 'A', 'b', 'B' ]. 약간 더 나은 질문은 현재 선호하는 언어 (C #)에서 휴식이 필수 이며 암시적인 오류 는 없습니다 . 잊어 버리는 break것은 구문 오류입니다 ... : \
KutuluMike

1
실제 코드를 통한 대체는 종종 파서 구현에서 발견됩니다. case TOKEN_A: /*set flag*/; case TOKEN_B: /*consume token*/; break; case TOKEN_C: /*...*/
OrangeDog

1
@ BlueRaja-DannyPflughoeft : C는 컴파일하기 쉽게 설계되었습니다. "경우 점프를 방출 break보다 구현하는 매우 간단한 규칙이다 어디서나 존재" "경우 점프를 방출하지 마십시오 fallthroughA의 존재 switch".
Jon Purdy

답변:


95

C는 switch이 형식으로 진술 을 한 최초의 언어 중 하나였으며 다른 모든 주요 언어는 C 언어를 기본값으로 유지하기로 선택했습니다. 모든 사람들이 익숙했던 행동을 유지하는 것보다 덜 중요합니다.

C가 그런 식으로 설계된 이유는 아마도 "휴대용 어셈블리"라는 C의 개념에서 비롯된 것입니다. 이 switch명령문은 기본적으로 브랜치 테이블 의 추상화 이며, 브랜치 테이블에도 암시적인 폴스 루가 있으며이를 피하려면 추가 점프 명령이 필요합니다.

그러니까 기본적으로, C의 디자이너는 또한 기본 당 어셈블러 의미를 유지하기로 결정했습니다.


3
VB.NET은 언어를 변경 한 언어의 예입니다. 케이스 조항으로 유입 될 위험은 없습니다.
찌를

1
Tcl은 다음 절로 넘어 가지 않는 또 다른 언어입니다. 사실상 특정 유형의 프로그램을 작성할 때 유용한 C와 달리 실제로는 그렇게하고 싶지 않았습니다.
Donal Fellows

4
C의 조상 언어 B 와 이전 BCPL 은 모두 스위치 문을 가지고있다. BCPL은 그것을 철자했다 switchon.
Keith Thompson

루비의 사례 진술은 실패하지 않습니다. 단일에 두 개의 테스트 값을 가짐으로써 유사한 동작을 얻을 수 있습니다 when.
Nathan Long

86

그 언어로 된 문장 switch의 대안이 아니기 때문 입니다 if ... else.

를 사용 switch하면 한 번에 둘 이상의 조건을 일치시킬 수 있으며 경우에 따라 높이 평가됩니다.

예:

public Season SeasonFromMonth(Month month)
{
    Season season;
    switch (month)
    {
        case Month.December:
        case Month.January:
        case Month.February:
            season = Season.Winter;
            break;

        case Month.March:
        case Month.April:
        case Month.May:
            season = Season.Spring;
            break;

        case Month.June:
        case Month.July:
        case Month.August:
            season = Season.Summer;
            break;

        default:
            season = Season.Autumn;
            break;
    }

    return season;
}

15
more than one block ... unwanted in most cases나는 당신에 동의하지 않습니다.
Satish Pandey

2
@DanielB 언어에 따라 다릅니다. C # 스위치 문은 그러한 가능성을 허용하지 않습니다.
Andy

5
@ 앤디 우리는 여전히 실패에 대해 이야기하고 있습니까? C # switch 문은 분명히 허용합니다 (3 번째 예를 확인하십시오) break. 그러나 네, 내가 아는 한, 더프의 장치는 C #에서 작동하지 않습니다.
Daniel B

8
@DanielB 우리는 그렇습니다. 그러나 컴파일러는 조건, 코드, 그리고 다른 조건을 허용하지 않습니다. 사이에 코드가없는 여러 조건을 쌓을 수 있거나 휴식이 필요합니다. 귀하의 링크에서 C# does not support an implicit fall through from one case label to another. 넘어 질 수는 있지만 실수로 휴식을 잊을 가능성은 없습니다.
Andy

3
@ Matsemann .. 이제 우리는 모든 것을 사용할 수 if..else있지만 어떤 상황 switch..case에서는 선호됩니다. 위의 예는 if-else를 사용하면 약간 복잡합니다.
Satish Pandey

15

이것은 C의 맥락에서 스택 오버플로에 대해 요청되었습니다. 왜 switch 문이 휴식을 취하도록 설계 되었습니까?

허용 된 답변을 요약하면 실수 일 수 있습니다. 대부분의 다른 언어는 아마도 C를 따랐을 것입니다. 그러나 C #과 같은 일부 언어는 대체를 허용하여이 문제를 해결 한 것으로 보입니다. 그러나 프로그래머가 명시 적으로 그렇게 말할 때만 가능합니다 (출처 : 위의 링크, C #을 직접 말하지 않습니다) .


Google Go는 IIRC와 동일한 기능을 수행합니다 (명시 적으로 지정하면 오류가 허용됨).
Leo

PHP도 마찬가지입니다. 그리고 결과 코드를 많이 볼수록 느낌이 덜 들립니다. 내가 좋아하는 /* FALLTHRU */위 @Tapio에 의해 연결이 질문에 코멘트를하는 증명 당신이 그것을 의미했다.
DaveP

1
goto case링크에서와 같이 C #에이라고 말하는 것이 왜 그렇게 잘 작동하는지 설명하지 않습니다. 위와 같이 여러 사례를 계속 쌓을 수 있지만 코드를 삽입하자마자 goto case whatever다음 사례로 넘어갈 명시 적이 거나 컴파일러 오류가 발생합니다. 두 가지 중 하나를 물어 보면 과실을 통해 실수로 넘어지는 것에 대해 걱정하지 않고 사례를 쌓을 수있는 기능은로 명시 적 넘어지는 것을 활성화하는 것보다 훨씬 더 나은 기능입니다 goto case.
Jesper

9

나는 예를 들어 대답 할 것이다. 1 년에 한 달의 일 수를 나열하려면 일부 달에 31, 30 및 1 28/29가 있음이 분명합니다. 이렇게 보일 것입니다.

switch(month) {
    case 4:
    case 6:
    case 9:
    case 11;
        days = 30;
        break;
    case 2:
        //Find out if is leap year( divisible by 4 and all that other stuff)
        days = 28 or 29;
        break;
    default:
        days = 31;
}

여러 사례가 동일한 효과를 가지며 모두 함께 그룹화 된 예입니다. if ... else if 구문이 아닌 break 키워드를 선택해야 할 이유가있었습니다 .

여기서 주목해야 할 것은 유사한 경우가 많은 switch 문이 if ... else if ... else if ... else 각각이 아니라 if (1, 2, 3)라는 것입니다. ... else if (4,5,6) else ...


2
귀하의 예는 if이익 이 없는 것보다 더 장황하게 보입니다 . 아마도 당신의 예가 이름을 부여했거나 넘어지기 외에 월별 작업을했다면, 넘어지기의 유용성이 더 분명할까요? (첫 번째 장소에 대한 의견이 없는지)
horatio

1
사실입니다. 그러나 나는 넘어지기가 사용될 수있는 사례를 보여 주려고했습니다. 각 달마다 개별적으로 수행해야 할 것이 있다면 훨씬 나을 것입니다.
Awemo

8

한 사례에서 다른 사례로 "경과"하는 두 가지 상황이 있습니다. 빈 사례는

switch ( ... )
{
  case 1:
  case 2:
    do_something_for_1_and_2();
    break;
  ...
}

그리고 비어 있지 않은 경우

switch ( ... )
{
  case 1:
    do_something_for_1();
    /* Deliberately fall through */
  case 2:
    do_something_for_1_and_2();
    break;
...
}

에 대한 참조에도 불구하고 Duff 's Device에 두 번째 경우에 대한 합법적 인 사례는 거의 없으며, 일반적으로 코딩 표준에 의해 금지되며 정적 분석 중에 플래그가 지정됩니다. 그리고 그것이 발견되는 곳은 break.

전자는 완벽하고 현명하고 일반적입니다.

솔직히 말해서, 나는 break 빈 케이스-바디가 넘어지고 비어 있지 않은 케이스는 독립형이라는 것을 알고 언어 파서 .

비논리적 인 것은 말할 것도없이 정의되지 않은, 지정되지 않은 또는 구현 정의 된 기능을 수정하는 대신, 언어에 새로운 (원치 않는) 기능과 잘못 정의 된 기능을 추가하는 것이 ISO C 패널에 더 집중되어있는 것이 유감입니다.


2
ISO C가 언어의 주요 변경 사항을 도입하지 않았 음을 감사합니다!
잭에 들리

4

강요하지 break않으면 수행하기 어려운 여러 가지 작업이 가능합니다. 다른 사람들은 그룹화 사례를 언급했으며 그 중 몇 가지 사소한 사례가 있습니다.

이 긴급하다 한 경우 break사용할 수 없습니다입니다 더프의 장치 . 이것은 필요한 비교 횟수를 제한하여 작업 속도를 높일 수있는 루프 를 " 언롤 " 하는 데 사용됩니다 . 처음 사용하면 완전히 롤업 된 루프로 인해 너무 느 렸던 기능이 허용되었다고 생각합니다. 경우에 따라 속도를 위해 코드 크기를 교환합니다.

사례에 break코드가있는 경우를 적절한 주석 으로 바꾸는 것이 좋습니다 . 그렇지 않으면 누군가가 missing을 수정하고 break버그를 발생시킵니다.


더프의 디바이스 예제 (+1)에 감사드립니다 . 나는 그것을 몰랐다.
trejder

3

원점 인 것처럼 보이는 C에서 switch명령문 의 코드 블록은 특별한 구성이 아닙니다. if명령문 아래의 블록과 마찬가지로 일반적인 코드 블록입니다 .

switch ()
{
}

if ()
{
}

casedefault특별히 관련이 블록 내부 점프 레이블이다 switch. 에 대한 일반적인 점프 레이블처럼 처리됩니다 goto. 여기서 중요한 한 가지 규칙이 있습니다. 점프 레이블은 코드 흐름을 방해하지 않고 코드의 거의 모든 곳에서 가능합니다.

일반적인 코드 블록으로서 복합 명령문 일 필요는 없습니다. 레이블도 옵션입니다. 다음은 switchC에서 유효한 진술입니다.

switch (a)
    case 1: Foo();

switch (a)
    Foo();

switch (a)
{
    Foo();
}

C 표준 자체는이를 예 (6.8.4.2)로 제공합니다.

switch (expr) 
{ 
    int i = 4; 
    f(i); 
  case 0: 
    i=17; 
    /*falls through into default code */ 
  default: 
    printf("%d\n", i); 
} 

인공 프로그램 조각에서 식별자가 i 인 객체는 자동 저장 기간 (블록 내)으로 존재하지만 초기화되지 않으므로 제어 표현식이 0이 아닌 값을 갖는 경우 printf 함수에 대한 호출은 결정되지 않은 값에 액세스합니다. 마찬가지로 함수 f에 대한 호출에 도달 할 수 없습니다.

또한 default점프 레이블이기도하므로 마지막 사례 일 필요없이 어느 곳에 나있을 수 있습니다.

이것은 또한 더프의 장치를 설명합니다 :

switch (count % 8) {
    case 0: do {  *to = *from++;
    case 7:       *to = *from++;
    case 6:       *to = *from++;
    case 5:       *to = *from++;
    case 4:       *to = *from++;
    case 3:       *to = *from++;
    case 2:       *to = *from++;
    case 1:       *to = *from++;
            } while(--n > 0);
}

왜 넘어 지는가? 때문에 코드의 일반 블록의 정상적인 코드 흐름, 가을-를 통해 다음 문장으로되어 예상 , 당신이 그것을 기대하는 것처럼 if코드 블록.

if (a == b)
{
    Foo();
    /* "Fall-through" to Bar expected here. */
    Bar();
}

switch (a)
{
    case 1: 
        Foo();
        /* Automatic break would violate expected code execution semantics. */
    case 2:
        Bar();
}

내 생각에 이것은 그 이유는 구현의 용이성 때문이었습니다. switch특별한 규칙을 염두에두고 블록 을 구문 분석하고 컴파일하는 데 특별한 코드가 필요하지 않습니다 . 다른 코드와 마찬가지로 구문 분석하고 레이블과 점프 선택 만 처리하면됩니다.

이 모든 것에서 흥미로운 후속 질문은 다음의 중첩 된 문장이 "완료"를 인쇄하는 경우입니다. 또는 아닙니다.

int a = 10;

switch (a)
{
    switch (a)
    {
        case 10: printf("Done.\n");
    }
}

C 표준은이 문제를 해결합니다 (6.8.4.2.4).

케이스 또는 기본 레이블은 가장 가까운 엔 클로징 스위치 명령문 내에서만 액세스 할 수 있습니다.


1

여러 사람들이 이미 여러 조건을 일치시키는 개념을 언급했으며, 이는 때때로 매우 가치가 있습니다. 그러나 여러 조건을 일치시키는 기능이 반드시 일치하는 각 조건에 대해 정확히 동일한 작업을 수행 할 필요는 없습니다. 다음을 고려하세요:

switch (someCase)
{
    case 1:
    case 2:
        doSomething1And2();
        break;

    case 3:
        doSomething3();

    case 4:
        doSomething3And4();
        break;

    default:
        throw new Error("Invalid Case");
}

여러 조건 세트가 일치하는 두 가지 방법이 있습니다. 조건 1과 2를 사용하면 동일한 코드 플롯으로 넘어 가고 정확히 동일한 작업을 수행합니다. 그러나 조건 3과 4에서는 모두 호출 doSomething3And4()끝나지만 3 번만 호출합니다 doSomething3().


파티에 늦었지만 함수 이름에서 알 수 있듯이 이것은 절대적으로 "1과 2"가 아닙니다. 트리거링 에서 코드로 이어질 수 있는 가지 상태가 있습니다. case 2함수 이름 doSomethingBecause_1_OR_2_OR_1AND2()또는 무언가 여야 합니다 (잘 작성된 코드를 작성하는 동안 불변이 두 가지 경우를 일치시키는 합법적 인 코드는 사실상 존재하지 않기 때문에 최소한 이것이되어야합니다 doSomethingBecause1or2())
Mike 'Pomax'Kamermans

즉, doSomething[ThatShouldBeDoneInTheCaseOf]1And[InTheCaseOf]2(). 논리적이고 /하거나 언급하지 않습니다.
Panzercrisis

나는 그렇지 않다는 것을 알고 있지만 사람들이 이런 종류의 코드에 대해 혼란스러워하는 이유를 감안할 때, "그리고"이 무엇인지 절대 설명해야합니다. 정확하게 설명되면, 답변 자체에 더 많은 설명이 필요하거나 그 모호성을 제거하기 위해 함수 이름을 다시 작성해야합니다 =)
Mike 'Pomax'Kamermans

1

두 가지 질문에 대답합니다.

C는 왜 휴식이 필요합니까?

그것은 "휴대용 어셈블러"로서 Cs 루트로 내려옵니다. 이와 같은 의사 코드가 일반적인 경우 :-

    targets=(addr1,addr2,addr3);
    opt = 1  ## or 0 or 2
switch:
    br targets[opt]  ## go to addr2 
addr1:
    do 1stuff
    br switchend
addr2:
    do 2stuff
    br switchend
addr3
    do 3stuff
switchend:
    ......

switch 문은 유사한 기능을 더 높은 수준으로 제공하도록 설계되었습니다.

휴식없는 스위치가 있습니까?

예, 이것은 매우 일반적이며 몇 가지 사용 사례가 있습니다.

먼저 여러 경우에 대해 동일한 조치를 수행 할 수 있습니다. 우리는 사례를 서로 쌓아서 이렇게합니다.

case 6:
case 9:
    // six or nine code

상태 머신에서 일반적으로 사용되는 또 다른 사용 사례는 한 상태를 처리 한 후 즉시 다른 상태로 들어가서 처리하려고한다는 것입니다.

case 9:
    crashed()
    newstate=10;
case 10:
    claim_damage();
    break;

-2

Break 문은 사용자가 가장 가까운 엔 클로징 스위치 (귀하의 경우) 동안, 수행 중, for 또는 foreach를 종료 할 수 있도록하는 점프 설명입니다. 그것만큼 쉽습니다.


-3

위의 모든 글 에서이 스레드의 주요 결과는 새로운 언어를 디자인하려는 경우 기본적으로 break명령문 을 추가 할 필요가 없으며 컴파일러는이를 마치 것처럼 처리한다고 생각합니다.

다음 사례로 계속 진행하려는 드문 경우를 원한다면 간단히 continue진술로 진술하십시오.

케이스 내부에 중괄호를 사용하는 경우에만 동일한 정확한 코드를 수행하는 여러 경우 중 몇 달이 지난 예제가-를 필요로하지 않고 항상 예상대로 작동하도록 개선 할 수 continue있습니다.


2
계속 진술과 관련된 귀하의 제안을 이해하지 못했습니다. Continue는 C와 C ++에서 매우 다른 의미를 지니고 있으며 새로운 언어가 C와 같지만 때로는 C와 마찬가지로 키워드를 사용하고 때로는이 대체 방법으로 키워드를 사용하면 혼란이 다 스릴 것이라고 생각합니다. 하나의 레이블이 지정된 블록에서 다른 레이블이있는 블록으로 넘어가는 데 문제가있을 수 있지만 공유 코드 블록으로 동일한 처리를하는 여러 사례를 사용하는 것이 좋습니다.
DeveloperDon

C는 여러 가지 목적으로 동일한 키워드를 사용하는 오랜 전통을 가지고 있습니다. 생각 *, &, static
idrougge
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.