Är det OK att använda goto i for/While loopar?

Permalänk

Är det OK att använda goto i for/While loopar?

Jag hade hittat en snabb och enkel lösning som faktiskt löste problemet, nämligen goto. Jag har använt det i en for-loop där jag hoppar upp ett steg om t.ex. fel nummer ges in. Nu är det så att om man trycker på * eller # så ska man hoppas hoppas upp igen så får man börja på nytt.

Min fråga är: Varför anses goto så dåligt i C/C++? Jag tycker goto är ganska bra i dessa typer när man behöver bara hoppa tillbaka till ett steg.

Min kodbit är denna:

show_digit(0); // Set the display to 0000 char abcd[4]; // create a array_char for input for(int i = 0; i <= 3; i++) { LOOPBACK: abcd[i]= kpd.waitForKey(); // add one char if (abcd[i] == '*' || abcd[i] == '#') // compare { LED(); // blink of fault number are pressed goto LOOPBACK; // go back one step in the loop } else { mydisplay.setDigit(0, 3-i, abcd[i] - '0', true); // show on display on reversed way e.g 0045 not 4500 } } // End of the loop // make abcd_int[] into one number, waint 3 seconds and return it delay(3000); return atoi(abcd);

Permalänk
Hedersmedlem

Främst har det väl fått dåligt rykte då folk som sagt tycker att det är smidigt att kunna hoppa lite hur som helst och därför gärna gör det väl ofta. Med följden att koden blir onödigt svårläst. Exemplet ovan är kanske inte så farligt, men ofta kan man ganska lätt lösa problemet utan goto (och därmed undvika att reta någon i onödan). Just här blir det ju till och med mindre kod:

for(int i = 0; i <= 3;) { abcd[i]= kpd.waitForKey(); // add one char if (abcd[i] == '*' || abcd[i] == '#') // compare { LED(); // blink of fault number are pressed } else { mydisplay.setDigit(0, 3-i, abcd[i] - '0', true); // show on display on reversed way e.g 0045 not 4500 i++; } }

Permalänk
Medlem

Det största problemet är att kod som mycket goto labels blir väldigt svårläst.

MISRA C 2004 förbjuder användandet av goto med motiveringen "These rules are in the interests of good structured programming".

MISRA-reglerna kan vara trevliga att titta på om man funderar över sin kodstruktur.

Visa signatur

Det man inte vet, det lider man inte av.

Permalänk

Jag tyckter det är ändå fel att verkligen banna goto så hårt. Dennise Ritchie som skapade C hade nog velat att goto ska vara accepterat. Annars hade han inte tagit med det.

Permalänk
Medlem

Jag anser personligen att goto är totalförbjudet och borde vara belagt med hård bestraffning. Det ger ett oförutsägbart kodflöde och skapar risker för fel i programkoden. Kan du koda på något annat sätt så gör det.

Visa signatur

Desktop: | Win10 | InWin 303 | ASUS TUF X570 | AMD Ryzen 5 3600 | Noctua NH-U12S (PP) | Intel 600p 256GB | Gigabyte GTX 670 | 32GB DDR4 2400Mhz | Corsair RM650x | 3x 1080 Screens |
Datacenter: | 1x Physical | 1x Virtual |
Laptop: | 2x |

Dell Certified Technician

Permalänk
Medlem

Boken The C programming language, 1979, K&R, p63, "it does seem that goto statements should be used sparingly if at all".

Permalänk
Skrivet av lassep1l1s:

Boken The C programming language, 1979, K&R, p63, "it does seem that goto statements should be used sparingly if at all".

Varför ens ta med det om det skulle undvikas som mycket som möjligt?

Permalänk
Skrivet av JamesBanana:

Jag anser personligen att goto är totalförbjudet och borde vara belagt med hård bestraffning. Det ger ett oförutsägbart kodflöde och skapar risker för fel i programkoden. Kan du koda på något annat sätt så gör det.

Typ shairalagar hehe

Vad menar du med "fel i programkoden". Menar du senare eller i just texten?
Jag menar om det blir buggar efter några år eller så om systemet körs.

Vem uppfan goto?

Permalänk

Jag typ måste köra goto i just detta fall för detta fungerar inte.

PROGRAM = kpd.waitForKey(); // waint until pressed key while (PROGRAM != '1' || PROGRAM != '2' || PROGRAM != '3') { PROGRAM = kpd.waitForKey(); // waint until pressed key } // continue

Jag vet det låter knäppt, men sanningen är att kpd.waitForKey(); fungerar inte i en while-loop

Permalänk
Medlem

Jag skerv en Pascalkompilator utan goto och lät den kompilera sig själv. Goto behövs inte om koden är lite strukturerad. Två gotos på 24 år som programmerare. Jag skäms.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Jag typ måste köra goto i just detta fall för detta fungerar inte.

Hur ser den fungerande varianten ut?

Skrivet av heretic16:

Vem uppfan goto?

Det har väl i princip alltid funnits (liknande hopp är ju vanliga på lägre nivåer (assembler till exempel)) men kommit i onåd när praxis för hur kod skall se ut har vuxit fram.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Jag tyckter det är ändå fel att verkligen banna goto så hårt. Dennise Ritchie som skapade C hade nog velat att goto ska vara accepterat. Annars hade han inte tagit med det.

Jag expanderar citatet ovan lite, från andra utgåvan av The C programming language skriven av just Ritchie och Kernighan. Först introduktionen av konceptet:

Skrivet av K&R:

C provides the infinitely-abusable `goto` statement, and labels to branch to. Formally, the `goto` statement is never necessary, and in practice it is almost always easy to write code without it. We have not used `goto` in this book.

Nevertheless, there are a few situations where `goto`s may find a place. The most common is to abandon processing in some deeply nested structure, such as breaking out of two or more loops at once. The break statement cannot be used directly since it only exits from the innermost loop.

This organization is handy if the error-handling code is non-trivial, and if errors can occur in
several places.

Och expansionen av det som nämndes ovan:

Skrivet av K&R:

With a few exceptions like those cited here, code that relies on `goto` statements is generally harder to understand and to maintain than code without `goto`s. Although we are not dogmatic about the matter, it does seem that goto statements should be used rarely, if at all.

Sedan 2011 är det rätt omöjligt att få svar från Ritchie gällande vad han tycker om saker, men hans skrifter finns ju kvar, varav just "The C programming language" troligen är den mest lästa. Jag rekommenderar återigen, liksom i din förra tråd, att du plockar upp den på ett bibliotek för läsning om du är intresserad av C.

Skrivet av GreveFrog:

Jag anser personligen att goto är totalförbjudet och borde vara belagt med hård bestraffning. Det ger ett oförutsägbart kodflöde och skapar risker för fel i programkoden. Kan du koda på något annat sätt så gör det.

"Totalförbud" kanske kan vara en bra första approximation i de flesta språk, då det är sällan som `goto` faktiskt gör saker enklare. Känner man "här vore det nog bra med en `goto`" i ett "vanligt" program så finns det en risk för att man har skapat en bakvänd logik till att börja med. Använder man `goto`-konstruktioner som substitut för vanliga loopar så är man på djupt vatten.

Med det sagt så används det flitigt i exempelvis Linuxkärnan — se exempelvis kapitel 7 i Linux kernel coding style [kernel.org]. Jag har (minst sagt) tillräckligt mycket respekt för de som gjort dessa designval i denna kontext, så jag bannar inte dem för att använda `goto`, men som sagt: generellt är totalförbud en rätt vettig utgångspunkt, tills man vet att och när de är bra att använda .

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Skrivet av Elgot:

Hur ser den fungerande varianten ut?

Så här:
http://pastebin.com/AfDdiNun

När jag använder goto så är det oftast som att bryta mig loss eller repitera en loop. Eller som nu där while inte fungerar med biblioteket av en märklig anledning. Annars fungerar det mycket bra.

Permalänk
Hedersmedlem
Skrivet av lassep1l1s:

Två gotos på 24 år som programmerare. Jag skäms.

Då gissar jag att du har lyckats undvika BASIC .

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Inaktiv

Det beror på språk, försök koda assembler utan att använda gotoinstruktioner av olika slag den som kan.

Allmänt idag kan man dock i de flesta nya språk klara sig bra utan goto och man bör istället skriva om koden.
Det finns dock många språk som man fortfarande måste använda gotos i som VBA, VB6 och assembler. Och även om man kodar nyare saker så använder man ofta äldre komponenter där det är svårt att motivera att man ska utveckla nya komponenter i ett nytt språk för några hundra tusen.

Skrivet av phz:

Då gissar jag att du har lyckats undvika BASIC .

Precis även om Basic är ett samlingsnamn där man i de nya basic språken klarar sig bra utan gotos.
VB.Net, nyare Casio-miniräknare med basic etc.

Permalänk
Hedersmedlem
Skrivet av heretic16:

När jag använder goto så är det oftast som att bryta mig loss eller repitera en loop.

Tipsar om att undersöka `continue` och `break` närmare, och samspelet mellan olika typer av loopkonstruktioner och när de passar bäst.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem
Skrivet av heretic16:

Jag typ måste köra goto i just detta fall för detta fungerar inte.

PROGRAM = kpd.waitForKey(); // waint until pressed key while (PROGRAM != '1' || PROGRAM != '2' || PROGRAM != '3') { PROGRAM = kpd.waitForKey(); // waint until pressed key } // continue

Jag vet det låter knäppt, men sanningen är att kpd.waitForKey(); fungerar inte i en while-loop

Det kommer garanterat att löna sig att undersöka varför det inte fungerar med en while-loop istället för att börja använda goto som standard. Börja med att felsöka, tex genom att printa vad PROGRAM innehåller, nu vet vi ju inte ens varför/hur det inte fungerar.

Permalänk
Hedersmedlem
Skrivet av heretic16:

Jag typ måste köra goto i just detta fall för detta fungerar inte.

PROGRAM = kpd.waitForKey(); // waint until pressed key while (PROGRAM != '1' || PROGRAM != '2' || PROGRAM != '3') { PROGRAM = kpd.waitForKey(); // waint until pressed key } // continue

Jag vet det låter knäppt, men sanningen är att kpd.waitForKey(); fungerar inte i en while-loop

Exakt vad är det som inte fungerar? Om du vill läsa in på nytt om PROGRAM är något annat än '1', '2' eller '3' borde du nog se över det där while-kriteriet.

Permalänk
Skrivet av Elgot:

Exakt vad är det som inte fungerar? Om du vill läsa in på nytt om PROGRAM är något annat än '1', '2' eller '3' borde du nog se över det där while-kriteriet.

Jadu. Det händer inget.
Tänkte inte på att jag kan printa, men jag ska ge ett försök och återkomma. Jag tar bort goto nu och testar.

Permalänk
Hedersmedlem

Halv-OT angående Linux: jag gjorde en väldigt snabb, halvful koll och hittade ungefär 116700 gotos (bland 12 180 728 rader C-kod), dvs ungefär 1/100 rader kod i Linux-kärnan är en goto.
(find /usr/src/linux-3.17.2 -name '*.c' -exec grep -P '^\s*goto\s+' {} + | wc -l)

De används som sagt väldigt mycket, framför allt där de avslutar funktioner (typ goto out, goto error och liknande) med labels i slutet av funktionen, som t ex frigör resurser och liknande innan return.

Skrivet av heretic16:

Jadu. Det händer inget.
Tänkte inte på att jag kan printa, men jag ska ge ett försök och återkomma. Jag tar bort goto nu och testar.

Har du samma kriterium i while-satsen? Tänk efter på vad den gör om PROGRAM == '2', till exempel.
(Ha i åtanke att om den första ger true så kollas aldrig de andra, dvs true || false || false ger true, medans false || false || true också ger true.)

Visa signatur

Asus B550M-Plus / Ryzen 5800X / 48 GB 3200 MHz CL14 / Asus TUF 3080 OC / WD SN850 1 TB, Kingston NV1 2 TB + HDDs / Corsair RM650x / Acer XB271HU (1440p165) / LG C1 55"
Mobil: Moto G200

Permalänk
Hedersmedlem
Skrivet av heretic16:

Jadu. Det händer inget.
Tänkte inte på att jag kan printa, men jag ska ge ett försök och återkomma. Jag tar bort goto nu och testar.

Fundera på vad som händer om PROGRAM till exempel sätts till '1' före while-satsen...

Permalänk
Medlem
Skrivet av heretic16:

Typ shairalagar hehe

Vad menar du med "fel i programkoden". Menar du senare eller i just texten?
Jag menar om det blir buggar efter några år eller så om systemet körs.

Vem uppfan goto?

Med fel menar jag bland annat buggar, men också logiska och semantiska fel mm.

Jag skulle tro att goto är en direkt referens till assemblerinstruktionen jmp (jump).

Problemet är att när man kör genom en kompilator har man inte den strikta kontrollen på programkoden som man har när man skriver assemblerkod manuellt, det kan alltså uppstå fel under kompileringen beroende på hur kompilatorn arbetar. Enda sättet att veta om det har uppstått är genom att manuellt granska assemblerkoden som genereras eller att testa väldigt noggrant. Båda sättet är mycket kostsamma och därav bör Goto-instruktioner undvikas när det finns fungerande alternativ.

Visa signatur

Desktop: | Win10 | InWin 303 | ASUS TUF X570 | AMD Ryzen 5 3600 | Noctua NH-U12S (PP) | Intel 600p 256GB | Gigabyte GTX 670 | 32GB DDR4 2400Mhz | Corsair RM650x | 3x 1080 Screens |
Datacenter: | 1x Physical | 1x Virtual |
Laptop: | 2x |

Dell Certified Technician

Permalänk
Hedersmedlem
Skrivet av JamesBanana:

Problemet är att när man kör genom en kompilator har man inte den strikta kontrollen på programkoden som man har när man skriver assemblerkod manuellt, det kan alltså uppstå fel under kompileringen beroende på hur kompilatorn arbetar. Enda sättet att veta om det har uppstått är genom att manuellt granska assemblerkoden som genereras eller att testa väldigt noggrant. Båda sättet är mycket kostsamma och därav bör Goto-instruktioner undvikas när det finns fungerande alternativ.

Inte för att säga emot, men har du några källor på att goto är extra besvärligt? Så är kanske fallet, men att kräva noggrann testning av kod som innehåller goto men glatt lita på kompilatorn i andra fall låter lite märkligt.

Permalänk
Skrivet av Elgot:

Fundera på vad som händer om PROGRAM till exempel sätts till '1' före while-satsen...

Det fungerar nu

char key = kpd.waitForKey(); // waint untill key are pressed while(key == '0' || key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9') { key = kpd.waitForKey(); // waint untill key are pressed again }

Men man fick göra det lite annolunda än annat typ key != '#' || key != '*';

Permalänk
Hedersmedlem
Skrivet av heretic16:

Det fungerar nu

char key = kpd.waitForKey(); // waint untill key are pressed while(key == '0' || key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9') { key = kpd.waitForKey(); // waint untill key are pressed again }

Men man fick göra det lite annolunda än annat typ key != '#' || key != '*';

Tänk också på att char egentligen är som vilken siffra som helst. Det går därför till exempel bra att jämföra med andra värden, så ovanstående kan skrivas som

while(key >= '0' && key <= '9')

Permalänk
Hedersmedlem
Skrivet av heretic16:

Men man fick göra det lite annolunda än annat typ key != '#' || key != '*';

if (key != '#' || key != '*') { ... } else { /* Når vi någonsin detta block? För vilket värde på `key`? Testa med olika * värden i tanken. Om vi inte kan nå till `else`, vad säger då egentligen * `if`-villkoret? */ }

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Skrivet av phz:

if (key != '#' || key != '*') { ... } else { /* Når vi någonsin detta block? För vilket värde på `key`? Testa med olika * värden i tanken. Om vi inte kan nå till `else`, vad säger då egentligen * `if`-villkoret? */ }

Inom hårdvaruprogrammering så undviks det väldigt mycket else och else if.
Tro mig, det har faktiskt visat sig ganska enkelt att programmera så.

Permalänk
Skrivet av Elgot:

Tänk också på att char egentligen är som vilken siffra som helst. Det går därför till exempel bra att jämföra med andra värden, så ovanstående kan skrivas som

while(key >= '0' && key <= '9')

Det där tänkte jag inte på heller att man kan tolka '0' som en siffra också

Så jag kan jämföra 0 och '0' ?

Permalänk
Hedersmedlem
Skrivet av heretic16:

Det där tänkte jag inte på heller att man kan tolka '0' som en siffra också

Så jag kan jämföra 0 och '0' ?

Jadå, men de är inte lika (det är därför man till exempel ser key - '0'). Titta i en ascii-tabell (eller skriv ut som int eller liknande) för att se vilka värde de egentligen har. '0' är till exempel 48, men '0', '1', ..., '9' kommer efter varandra, så om man subtraherar '0' blir de 0 till 9.

Permalänk
Hedersmedlem
Skrivet av Thomas:

Halv-OT angående Linux: …

Kontrar med hel-OT gällande `grep` :

Skrivet av Thomas:

find /usr/src/linux-3.17.2 -name '*.c' -exec grep -P '^\s*goto\s+' {} + | wc -l

"Do one thing and do it well" — men GNU grep har utöver detta fått lite extra fläsk på benen, så ovanstående är i funktion ekvivalent med:

grep -rP '^\s*goto\s+' --include='*.c' /usr/src/linux-3.17.2 | wc -l

där vi "sparar" (minst) en processförgrening. `-r` är väldigt användbart, så jag antar att resonemanget bakom att lägga till `--exclude`/`--include` och kompisar var att de ändå är triviala att implementera när rekursiv filletning väl är på plats.

`grep` kan för den delen också räkna rader själv med `-c`, men det ger busigt resultat med multipla filer.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.