일부 C 코드를 정리하고 데비안 패키지를 빌드하려고 합니다. makefile은 -Wall을 사용하도록 설정되었지만 디빌드는 -Wpedantic을 사용합니다.
코드가 데이터 포인터를 함수 포인터로 변환하기 때문에 이는 "좋은 일"입니다(ISO C에서는 허용되지 않으며 매우 위험합니다... 일부 아키텍처에서는 데이터와 코드가 다른 주소 공간에 있을 수 있음).
그러나 코드는 스위치 문에서도 범위를 사용합니다. 이는 gcc(및 clang) 확장이므로 -Wpedantic에 문제가 있습니다.
case QNAP_PICSTS_SYS_TEMP_0 ... QNAP_PICSTS_SYS_TEMP_70:
(#define은 열거형이 아닙니다)
스위치 범위의 사용은 커널에서 일반적이며 일부 if/else 논리와 함께 기본값을 사용하도록 다시 코딩할 수 있지만 실제로는 더 느립니다(점프 테이블 대 코드).
그러면 1번은 이 기능만 허용하는 옵션이 있고 2번(보너스 포인트용)은 데비안 패키지 규칙에서 어떻게 설정하나요?
옵션은 다음과 같습니다.
http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Warning-Options.html
답변1
나는 광범위한 범위를 사용하여 switch/case/default의 컴파일러 출력을 살펴보았습니다. if/else에서 구현된 동일한 코드와 비교했습니다.소스 코드).
이름과는 별개로, 이는 정확히 동일한 바이너리 코드(Godbolt의 컴파일러 브라우저), debian 12의 기본 GCC 12.2와 함께 사용되는 경우 및 dpkg-buildflags
debian에서 사용하는 관련 콘텐츠와 함께 사용되는 경우.
다행스럽게도 최신 컴파일러는 간단한 것을 보고 if(state == constant) expr; else if(state == other_constant)…
길이가 짧으면 점프 테이블로 변환할 수 있습니다. 따라서 코드는 스위치/케이스와 완전히 동일합니다. 이것은 이미 발생하고 -O1
있으므로 위험한 최적화는 아닙니다.
그리고 더욱 놀랍게도 get과 같은 구조는 case 5 ... 74:
75와의 비교로 변환됩니다(점프 테이블이 이미 5 미만의 사례를 처리한 후).
따라서 실제로 는 와 보다 더 깔끔해 보인다고 생각하지 않는 한 현대 코드에서 switch
를 사용할 이유가 없습니다 . 나는 모른다 - 그리고 나는 당신의 컴파일러도 이것을 하지 않을 것이라고 생각한다: 더 엄격한 범위 지정은 블록을 사용하여 달성된다.case
case CONSTANT: … break;
if(state == CONSTANT){ … }
한 가지 예외가 있는데, 이는 물론 case:
없이 기본적으로 없이 break
시뮬레이션할 수 있다는 것 입니다 .goto
goto
switch(state) {
case 1:
value += 0.2f;
case 2:
retval = 1.0f/value;
break
// …
}
가설과 동일
if (state == 1) {
value += 0.2f;
goto label_in_two;
}
else if(state == 2)
label_in_two:
retval = 1.0f/value;
}
(우리 모두는 의지를 잊어버림으로 인해 발생하는 실수를 알고 있습니다. 그렇지 않은 경우 break;
의지를 사용하면 동일한 문제가 발생합니다.goto
매우주의 깊은. )
이것은 실제로 유한 상태 기계를 구축하는 사람들을 위한 편리한 구성이며 범위와 함께 사용하기 위한 것이 아니라는 점을 이해하시기 바랍니다. switch
오래되지 않은 컴파일러에 비해 전혀 이점이 없으며 if/else if
명확한 제어가 없는 경우 스트림에는 수많은 오류 소스가 있습니다.
그래서 저는 돌이켜보면 현대 컴파일러 최적화에 대한 지식을 바탕으로 GNU의 case
범위 기능이 잘못 고려되었다고 생각합니다. 따라서 경고는 합리적이었습니다.
그래서 저는 이 경고를 제거하는 올바른 방법은 switch
//를 // 구문으로 변환하는 것이라는 결론에 도달했습니다.case
default
if
else if
else