[c] 스위치 케이스에서 유효하지만 쓸모없는 구문?

약간의 오타를 통해 실수 로이 구성을 찾았습니다.

int main(void) {
    char foo = 'c';

    switch(foo)
    {
        printf("Cant Touch This\n");   // This line is Unreachable

        case 'a': printf("A\n"); break;
        case 'b': printf("B\n"); break;
        case 'c': printf("C\n"); break;
        case 'd': printf("D\n"); break;
    }

    return 0;
}

것 같다 printf의 상단에 switch문이 완전히 도달도 유효하지만입니다.

도달 할 수없는 코드에 대한 경고조차없이 깨끗한 컴파일을 얻었지만 이것은 의미가없는 것 같습니다.

컴파일러가이 코드를 도달 할 수없는 코드로 표시해야합니까?
이것이 어떤 목적에도 도움이됩니까?



답변

아마도 가장 유용하지는 않지만 완전히 쓸모없는 것은 아닙니다 . 이를 사용하여 switch범위 내에서 사용 가능한 로컬 변수를 선언 할 수 있습니다 .

switch (foo)
{
    int i;
case 0:
    i = 0;
    //....
case 1:
    i = 1;
    //....
}

표준 ( N1579 6.8.4.2/7)에는 다음 샘플이 있습니다.

예 인공 프로그램 조각에서

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도달 할 수 없습니다.

PS BTW, 샘플이 유효한 C ++ 코드가 아닙니다. 이 경우 ( N4140 6.7/3, 강조 광산) :

변수가 스칼라 유형 , 사소한 기본 생성자가있는 클래스 유형 및 사소한 소멸자를 갖지 않는 한, 자동 저장 기간이있는 변수가 범위 내에 있지 않은 지점에서 범위 내에있는 지점으로 90 이 점프하는 프로그램 은 , 이러한 유형 중 하나의 cv 규정 버전 또는 이전 유형 중 하나의 배열이며 초기화 프로그램 (8.5) 없이 선언됩니다 .


90) switch진술 의 조건 에서 사례 라벨로의 이전은이 점에서 점프로 간주된다.

교체 그래서 int i = 4;으로 int i;만든다 그것은 유효한 C ++.


답변

이것이 어떤 목적에도 도움이됩니까?

예. 명령문 대신 첫 번째 레이블 앞에 선언을 넣으면 완벽하게 이해할 수 있습니다.

switch (a) {
  int i;
case 0:
  i = f(); g(); h(i);
  break;
case 1:
  i = g(); f(); h(i);
  break;
}

선언 및 명령문에 대한 규칙은 일반적으로 블록에 대해 공유되므로 명령문에 대한 명령문도 허용하는 규칙과 동일합니다.


또한 첫 번째 명령문이 루프 구문 인 경우 루프 본문에 대소 문자 레이블이 표시 될 수 있습니다.

switch (i) {
  for (;;) {
    f();
  case 1:
    g();
  case 2:
    if (h()) break;
  }
}

더 읽기 쉬운 코드 작성 방법이 있지만 완벽하게 유효하고 f()호출에 도달 하는 경우 이와 같은 코드를 작성하지 마십시오 .


답변

Duff ‘s Device 라는 유명한 사용법이 있습니다.

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

여기에 우리가 가리키는 버퍼를 복사 from가 가리키는 버퍼에 to. count데이터 인스턴스를 복사 합니다.

do{}while()문은 전에 먼저 시작 case레이블과 case레이블이에 포함된다 do{}while().

이렇게하면 do{}while()루프 끝에서 발생하는 조건 분기의 수가 약 4 배로 줄어 듭니다 (이 예에서는 상수를 원하는 값으로 조정할 수 있음).

이제 옵티마이 저가 때때로이를 위해 (특히 스트리밍 / 벡터화 된 명령어를 최적화하는 경우)이를 수행 할 수 있지만 프로파일 가이드 최적화가 없으면 루프가 클 것으로 예상되는지 알 수 없습니다.

일반적으로 변수 선언이 발생하여 모든 경우에 사용될 수 있지만 스위치가 끝난 후에는 범위를 벗어납니다. (초기화는 건너 뜁니다.)

또한 스위치 별이 아닌 제어 흐름을 사용하면 위에서 설명한 것처럼 또는 스위치 블록의 해당 섹션으로 이동할 수 있습니다 goto.


답변

Linux에서 gcc를 사용한다고 가정하면 4.4 이하 버전을 사용하는 경우 경고 메시지가 표시됩니다.

gcc 4.4 이후 에는 -Wunreachable-code 옵션 이 제거되었습니다 .


답변

변수 선언뿐만 아니라 고급 점프도 가능합니다. 스파게티 코드를 사용하지 않는 경우에만 잘 활용할 수 있습니다.

int main()
{
    int i = 1;
    switch(i)
    {
        nocase:
        printf("no case\n");

        case 0: printf("0\n"); break;
        case 1: printf("1\n"); goto nocase;
    }
    return 0;
}

인쇄물

1
no case
0 /* Notice how "0" prints even though i = 1 */

switch-case는 가장 빠른 제어 흐름 절 중 하나입니다. 따라서 프로그래머에게 매우 유연해야하며 때로는 이와 같은 경우가 포함됩니다.


답변

switch명령문 내의 코드 또는 case *:레이블이이 코드 내의 위치 에 대한 구조적 제한은 거의 없습니다 . * 이것은 더프 장치 와 같은 프로그래밍 트릭을 가능하게합니다. 하나의 구현은 다음과 같습니다.

int n = ...;
int iterations = n/8;
switch(n%8) {
    while(iterations--) {
        sum += *ptr++;
        case 7: sum += *ptr++;
        case 6: sum += *ptr++;
        case 5: sum += *ptr++;
        case 4: sum += *ptr++;
        case 3: sum += *ptr++;
        case 2: sum += *ptr++;
        case 1: sum += *ptr++;
        case 0: ;
    }
}

보시다시피 레이블 switch(n%8) {case 7:레이블 사이의 코드에 도달 할 수 있습니다.


* 으로 supercat 고맙게도 코멘트 지적 : C99,도 때문에 goto나 라벨이 (그것을 할 수 case *:VLA 선언을 포함하는 선언의 범위 내에서 나타날 수 라벨 없음). 따라서 레이블 배치에 구조적 제한 이 없다고 말하는 것은 올바르지 않습니다 case *:. 그러나 더프의 장치는 C99 표준보다 오래되었으며 VLA에 의존하지 않습니다. 그럼에도 불구하고, 나는 이것 때문에 첫 문장에 “가상”을 삽입해야한다는 느낌이 들었다.


답변

경고를 생성 하는 데 필요한 gcc옵션 과 관련된 답변을 얻었습니다 -Wswitch-unreachable.이 답변은 유용성 / 가치 부분 을 자세히 설명하는 것 입니다.

C11§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도달 할 수 없습니다.

매우 설명이 필요합니다. 이를 사용하여 switch명령문 범위 내에서만 사용 가능한 로컬 범위 변수를 정의 할 수 있습니다 .