GNU/Linux >> Znalost Linux >  >> Linux

Proč někteří programátoři jádra používají goto místo jednoduchých smyček while?

Historický kontext: Měli bychom si pamatovat, že Dijkstra napsal Goto považováno za škodlivé v roce 1968, kdy mnoho programátorů používalo goto jako náhrada za strukturované programování (if , while , for , atd.).

Je to o 44 let později a je vzácné najít toto použití goto v divočině. Strukturované programování již dávno zvítězilo.

Analýza případu:

Příklad kódu vypadá takto:

    SETUP...
again:
    COMPUTE SOME VALUES...
    if (cmpxchg64(ptr, old_val, val) != old_val)
        goto again;

Strukturovaná verze vypadá takto:

SETUP...
do {
    COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);

Když se podívám na strukturovanou verzi, okamžitě si říkám:"to je smyčka". Když se podívám na goto verzi, považuji to za přímku s případem „zkuste to znovu“ na konci.

goto verze má obě SETUP a COMPUTE SOME VALUES na stejném sloupci, což zdůrazňuje, že většinu času řídicí tok prochází oběma. Strukturovaná verze uvádí SETUP a COMPUTE SOME VALUES na různých sloupcích, což zdůrazňuje, že kontrola jimi může procházet odlišně.

Otázkou je, jaký důraz chcete v kódu klást? Můžete to porovnat s goto pro zpracování chyb:

Strukturovaná verze:

if (do_something() != ERR) {
    if (do_something2() != ERR) {
        if (do_something3() != ERR) {
            if (do_something4() != ERR) {
                ...

Přejít na verzi:

if (do_something() == ERR)  // Straight line
    goto error;             // |
if (do_something2() == ERR) // |
    goto error;             // |
if (do_something3() == ERR) // |
    goto error;             // V
if (do_something4() == ERR) // emphasizes normal control flow
    goto error;

Vygenerovaný kód je v podstatě stejný, takže to můžeme považovat za typografický problém, jako je odsazení.


Velmi dobrá otázka a myslím, že definitivní odpověď může poskytnout pouze autor (autoři). Přidám trochu své spekulace tím, že řeknu, že to mohlo začít tím, že se to začalo používat pro zpracování chyb, jak vysvětlil @Izkata, a brány pak byly otevřené pro použití i pro základní smyčky.

Použití zpracování chyb je podle mého názoru legitimní v programování systémů. Funkce postupně přiděluje paměť, a pokud dojde k chybě, bude goto příslušný štítek pro uvolnění zdrojů v opačném pořadí od tohoto bodu.

Pokud tedy dojde k chybě po prvním přidělení, přeskočí se na poslední štítek chyby, aby se uvolnil pouze jeden zdroj. Podobně, pokud k chybě dojde po poslední alokaci, přeskočí na první štítek chyby a spustí se odtamtud, přičemž uvolní všechny prostředky až do konce funkce. Takový vzor pro zpracování chyb je stále třeba používat opatrně, zejména při úpravě kódu, valgrind a unit testy jsou vysoce doporučeny. Ale je pravděpodobně čitelnější a udržitelnější než alternativní přístupy.

Jedno zlaté pravidlo použití goto je vyhnout se tzv. špagetovému kódu. Zkuste nakreslit čáry mezi jednotlivými goto prohlášení a jeho příslušné označení. Pokud se čáry kříží, dobře, překročili jste čáru :). Takové použití goto je velmi špatně čitelný a je častým zdrojem obtížně sledovatelných chyb, protože by se nacházely v jazycích jako BASIC, které se na něj spoléhaly při řízení toku.

Pokud uděláte jen jednu jednoduchou smyčku, nedojde k překřížení čar, takže je stále čitelný a udržovatelný a stává se převážně záležitostí stylu. Vzhledem k tomu, že je lze stejně snadno provést pomocí klíčových slov smyčky poskytnutých v jazyce, jak jste uvedli ve své otázce, mým doporučením by bylo vyhnout se použití goto pro smyčky, jednoduše proto, že for , do/while nebo while konstrukce jsou svým designem elegantnější.


V případě tohoto příkladu se domnívám, že šlo o dovybavení podpory SMP do kódu, který byl původně napsán způsobem, který není bezpečný pro SMP. Přidání goto again; cesta je mnohem jednodušší a méně invazivní než restrukturalizace funkce.

Nemohu říci, že se mi tento styl moc líbí, ale také si myslím, že je scestné vyhýbat se goto z ideologických důvodů. Jeden speciální případ goto použití (odlišné od tohoto příkladu) je kde goto se používá pouze k pohybu vpřed ve funkci, nikdy zpět. Tato třída použití nikdy nevede ke konstrukci smyček vznikajících z goto a je to téměř vždy ten nejjednodušší a nejjasnější způsob, jak implementovat potřebné chování (což je obvykle vyčištění a vrácení chyby).


Linux
  1. Proč se `při Ifs=Read` tak často používá místo `ifs=; Při čtení..`?

  2. Linux – proč používáme Su – a nejen Su?

  3. Použít $[ Expr ] místo $(( Expr ))?

  1. Chyba „mapa se používá“ při odstraňování vícecestného zařízení v CentOS/RHEL

  2. Proč fclose(NULL) glibc způsobí chybu segmentace místo vracení chyby?

  3. Při přidávání textu před nějaký vzor použijte sed s ignorováním velkých a malých písmen

  1. Proč programátoři milují balení Linuxu

  2. Použijte systemd časovače místo cronjobů

  3. Proč používat shm_open?