Przez małą literówkę przypadkowo znalazłem ten konstrukt:
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;
}
Wygląda printf
na to, że górna część switch
instrukcji jest poprawna, ale również całkowicie nieosiągalna.
Mam czystą kompilację, nawet bez ostrzeżenia o nieosiągalnym kodzie, ale wydaje się to bezcelowe.
Czy kompilator powinien oznaczyć ten kod jako nieosiągalny?
Czy to w ogóle ma jakiś cel?
c
switch-statement
language-lawyer
Abelenky
źródło
źródło
-Wswitch-unreachable
goto
wchodzić i wychodzić z nieosiągalnej w inny sposób części, co może być przydatne w przypadku różnych włamań.switch
jest to tylko warunekgoto
z wieloma etykietami. Istnieją mniej więcej takie same ograniczenia dotyczące jego ciała, jak w przypadku zwykłego bloku kodu wypełnionego etykietami goto.Odpowiedzi:
Być może nie jest to najbardziej przydatne, ale nie całkowicie bezwartościowe. Możesz go użyć do zadeklarowania zmiennej lokalnej dostępnej w
switch
zakresie.Standard (
N1579 6.8.4.2/7
) ma następującą próbkę:PS BTW, próbka nie jest poprawnym kodem C ++. W takim przypadku (
N4140 6.7/3
podkreśl moje):Więc zastąpienie
int i = 4;
zeint i;
robi to ważny C ++.źródło
i
to, że jest inicjowane na 4, czego mi brakuje?static
, zostanie ona zainicjowana na zero, więc jest to również bezpieczne użycie.i = 4;
inicjalizację, więc nigdy nie ma ona miejsca.case
i zawsze musiałem używać różnych nazw w każdej z nichcase
lub definiować ją poza przełącznikiem.Tak. Jeśli zamiast instrukcji wstawisz deklarację przed pierwszą etykietą, może to mieć sens:
Reguły dotyczące deklaracji i instrukcji są ogólnie wspólne dla bloków, więc jest to ta sama reguła, która pozwala na to, że zezwala również na instrukcje tam.
Warto również wspomnieć, że jeśli pierwsza instrukcja jest konstrukcją pętli, etykiety przypadków mogą pojawić się w treści pętli:
Proszę nie pisać takiego kodu, jeśli istnieje bardziej czytelny sposób pisania, ale jest on całkowicie poprawny, a
f()
połączenie jest osiągalne.źródło
{ /*code*/ switch(x) { } }
może wyglądać na czystszy, ale jest również błędny .Znane jest zastosowanie tego urządzenia o nazwie Duff's Device .
Tutaj kopiujemy bufor wskazywany przez
from
do bufora wskazywanego przezto
. Kopiujemycount
wystąpienia danych.do{}while()
Oświadczenie zaczyna się przed pierwszymcase
znacznikiem, acase
znaczniki są osadzone wdo{}while()
.Zmniejsza to liczbę rozgałęzień warunkowych na końcu
do{}while()
pętli napotkanych z grubsza o współczynnik 4 (w tym przykładzie; stałą można dostosować do dowolnej wartości).Teraz optymalizatorzy mogą czasami zrobić to za Ciebie (szczególnie jeśli optymalizują instrukcje przesyłania strumieniowego / wektoryzacji), ale bez optymalizacji kierowanej profilem nie mogą wiedzieć, czy oczekujesz dużej pętli.
Zasadniczo mogą tam występować zmienne deklaracje i mogą być używane w każdym przypadku, ale po zakończeniu przełączania będą poza zakresem. (pamiętaj, że każda inicjalizacja zostanie pominięta)
Ponadto przepływ sterowania, który nie jest specyficzny dla przełącznika, może doprowadzić cię do tej sekcji bloku przełącznika, jak pokazano powyżej lub za pomocą
goto
.źródło
do {
icase 0:
nie ma znaczenia, oba służą do umieszczenia celu skoku na pierwszym*to = *from++;
.do {
jest bardziej czytelne. Tak, kłótnia o czytelność Urządzenia Duffa jest głupia i bezcelowa i prawdopodobnie jest to prosta droga do szaleństwa.Zakładając, że używasz gcc w Linuksie, dałoby ci ostrzeżenie, jeśli używasz 4.4 lub wcześniejszej wersji.
Opcja -Wunreachable-code została usunięta w gcc 4.4 i nowszych.
źródło
Nie tylko dla deklaracji zmiennych, ale także dla zaawansowanych skoków. Możesz go dobrze wykorzystać, jeśli tylko nie jesteś podatny na kod spaghetti.
Wydruki
Należy zauważyć, że skrzynka przełączników jest jedną z najszybszych klauzul przepływu sterowania. Musi więc być bardzo elastyczny dla programisty, co czasami obejmuje takie przypadki.
źródło
nocase:
idefault:
?i=4
się nie uruchamianocase
.Należy zauważyć, że praktycznie nie ma ograniczeń strukturalnych w kodzie w
switch
instrukcji ani w tym, gdziecase *:
etykiety są umieszczone w tym kodzie *. Dzięki temu możliwe są sztuczki programistyczne, takie jak urządzenie duffa , których jedna możliwa implementacja wygląda następująco:Widzisz, kod między etykietą
switch(n%8) {
acase 7:
etykietą jest zdecydowanie osiągalny ...* Jak supercat na szczęście zauważył w komentarzu : Od C99
goto
ani etykieta, ani etykieta (czy tocase *:
etykieta, czy nie) nie mogą pojawiać się w zakresie deklaracji zawierającej deklarację VLA. Nie można zatem powiedzieć, że nie ma żadnych ograniczeń strukturalnych dotyczących umieszczaniacase *:
etykiet. Jednak urządzenie duff wyprzedza standard C99 i i tak nie zależy od VLA. Niemniej jednak czułem się zmuszony wstawić „wirtualnie” do mojego pierwszego zdania z tego powodu.źródło
goto
nor nieswitch/case/default
może pojawiać się w zakresie dowolnego deklarowanego obiektu lub typu. Oznacza to skutecznie, że jeśli blok zawiera jakiekolwiek deklaracje obiektów lub typów tablic o zmiennej długości, wszelkie etykiety muszą poprzedzać te deklaracje. W standardzie występuje nieco myląca wersja, która sugerowałaby, że w niektórych przypadkach zakres deklaracji VLA rozciąga się na całość instrukcji przełączania; moje pytanie na ten temat znajduje się na stackoverflow.com/questions/41752072/ ...Masz odpowiedź związana z wymaganą
gcc
opcję-Wswitch-unreachable
generowania ostrzeżenia, to odpowiedź jest opracowanie na użyteczność / worthyness części.Cytując bezpośrednio
C11
, rozdział 6.8.4.2, ( moje podkreślenie )Co jest bardzo oczywiste. Można go użyć do zdefiniowania zmiennej o zasięgu lokalnym, która jest dostępna tylko w zakresie
switch
instrukcji.źródło
Możliwe jest zaimplementowanie za nim „półtorej pętli”, chociaż może nie być to najlepszy sposób:
źródło