2014-07-12 19 views
18

Czy możesz zerwać z instrukcją if lub czy spowoduje to awarie? Zaczynam poznawać C, ale wydaje się to kontrowersyjne. Pierwsze zdjęcie pochodzi z książki o C ("Head First C"), a fragment pokazuje kod napisany przez personel klas CS Harvard. Co się właściwie dzieje i ma coś wspólnego ze standardami C?"Przerwa;" z instrukcji "if"?

przerwy nie łamią się, jeśli instrukcje.

W dniu 15 stycznia 1990 r. AT z powodu długich połączeń telefonicznych z powodu awarii i 60.000 osób straciło usługi telefoniczne. Powód? Deweloper pracujący nad kodem C używanym na giełdach próbował użyć break, aby wykreślić instrukcję if. Ale break s nie wyrwać z if s. Zamiast tego program pomijał całą sekcję kodu i wprowadził błąd, który przerwał 70 milionów połączeń telefonicznych przez dziewięć godzin.

for (size = 0; size < HAY_MAX; size++) 
{ 
    // wait for hay until EOF 
    printf("\nhaystack[%d] = ", size); 
    int straw = GetInt(); 
    if (straw == INT_MAX) 
     break; 

    // add hay to stack 
    haystack[size] = straw; 
} 
printf("\n"); 
+2

Nie można "przełamać" z wyrażenia "if", dopóki 'if' nie znajdzie się wewnątrz pętli. – Ankur

+1

Tak działa język. Przerwa jest użyteczna tylko wtedy, gdy jest warunkowa i musi być warunkowa, musi znajdować się w instrukcji if, right –

+5

Zachowanie instrukcji 'break' jest dobrze określone i , ogólnie rzecz biorąc, dobrze zrozumiany. Niedoświadczony programista może powodować awarie, chociaż na wiele sposobów nie rozumie. Nadużycie instrukcji 'break' nie jest szczególne. –

Odpowiedz

28

break oddziałuje wyłącznie najbliższego pętli otaczającej lub przełącznik, czy to for, while lub do .. while typu. Jest często określany jako goto w przebraniu, ponieważ wszystkie pętle w C w rzeczywistości może być przekształcona w zestawie GOTOS Warunkowo:

for (A; B; C) D; 
// translates to 
A; 
goto test; 
loop: D; 
iter: C; 
test: if (B) goto loop; 
end: 

while (B) D;   // Simply doesn't have A or C 
do { D; } while (B); // Omits initial goto test 
continue;    // goto iter; 
break;    // goto end; 

Różnica polega na tym, continue i break interakcji z wirtualnymi etykiety automatycznie wprowadzane przez kompilator. Jest to podobne do tego, co robi return, jak wiesz, zawsze będzie przeskakiwało w przód w przepływie programu. Przełączniki są nieco bardziej skomplikowane, generując tablice etykiet i obliczone wartości gotówkowe, ale sposób, w jaki działa podział, jest podobny.

Błąd programowania, o którym mowa w zawiadomieniu, to nieporozumienie break jako interakcja z otaczającym blokiem, a nie z otaczającą pętlą. Rozważmy:

for (A; B; C) { 
    D; 
    if (E) { 
     F; 
     if (G) break; // Incorrectly assumed to break if(E), breaks for() 
     H; 
    } 
    I; 
} 
J; 

Ktoś myślał, biorąc pod uwagę taki kawałek kodu, który G spowoduje skok do I, ale skacze do J. Zamierzona funkcja użyje zamiast tego if (!G) H;.

1

To jest rzeczywiście konwencjonalna Zastosowanie rachunku break. Jeśli instrukcja break nie była zagnieżdżona w bloku if, pętla for mogła zostać kiedykolwiek wykonana tylko raz.

MSDN wymienia to jako ich example dla oświadczenia break.

0

Myślę, że to pytanie jest trochę niewyraźne - na przykład może być interpretowane jako pytanie o najlepsze praktyki w programowaniu pętli z if w środku. Postaram się odpowiedzieć na to pytanie za pomocą tej konkretnej interpretacji.

Jeśli masz if w pętli, to w większości przypadków chciałbyś się dowiedzieć, jak zakończyła się pętla - czy została "zerwana" przez if, czy też zakończyła się "naturalnie"?Tak, Twój przykładowy kod może być modyfikowany w ten sposób:

bool intMaxFound = false; 
for (size = 0; size < HAY_MAX; size++) 
{ 
    // wait for hay until EOF 
    printf("\nhaystack[%d] = ", size); 
    int straw = GetInt(); 
    if (straw == INT_MAX) 
    {intMaxFound = true; break;} 

    // add hay to stack 
    haystack[size] = straw; 
} 
if (intMaxFound) 
{ 
    // ... broken 
} 
else 
{ 
    // ... ended naturally 
} 

Problem z tym kodem jest, że oświadczenie if jest pochowany wewnątrz ciała pętli, a to wymaga pewnego wysiłku, aby odnaleźć i zrozumieć, co robi. Bardziej jasne (nawet bez oświadczenie break) wariant będzie:

bool intMaxFound = false; 
for (size = 0; size < HAY_MAX && !intMaxFound; size++) 
{ 
    // wait for hay until EOF 
    printf("\nhaystack[%d] = ", size); 
    int straw = GetInt(); 
    if (straw == INT_MAX) 
    {intMaxFound = true; continue;} 

    // add hay to stack 
    haystack[size] = straw; 
} 
if (intMaxFound) 
{ 
    // ... broken 
} 
else 
{ 
    // ... ended naturally 
} 

W tym przypadku można wyraźnie zobaczyć (tylko patrząc na pętli „cel”), że ta pętla może koniec przedwcześnie. Jeśli treść pętli jest tekstem wielostronicowym, napisanym przez kogoś innego, to dziękujesz jej autorowi za oszczędność czasu.

UPDATE:

Dzięki SO - to właśnie sugerował już odpowiedział question o awarii sieci telefonicznej & T AT w roku 1990. Chodzi o to ryzykowna decyzja twórców C użyć pojedynczego słowa zastrzeżonego break, aby wyjść z obu pętli i switch.

W każdym razie ta interpretacja nie wynika z kodu przykładowego w pierwotnym pytaniu, więc pozostawiam moją odpowiedź taką, jaka jest.

0

Jak już wspomniano, że włamanie oświadczenie działa tylko z przełączników i pętli. Oto inny sposób osiągnięcia tego, o co się pytamy. Reprodukuję https://stackoverflow.com/a/257421/1188057, tak jak nikt inny o tym nie wspomniał. To tylko sztuczka z pętlą do-while.

do { 
    // do something 
    if (error) { 
    break; 
    } 
    // do something else 
    if (error) { 
    break; 
    } 
    // etc.. 
} while (0); 

Choć wolałbym wykorzystania goto-oświadczenie.

+0

Podobnie jak urządzenie Duffa, wątpliwe jest, czy jest to eleganckie, czy okropne. Kombinacja 'do {} while (0)' jest często wykorzystywana do tworzenia makr przypominających wywołania funkcji, tak że dodanie ';' po nich nie tworzy dodatkowej pustej instrukcji. –

Powiązane problemy