Permalänk
Medlem

Switch-sats i C

Hej!

Gör en uppgift med en Switch-sats i C. Programmet fungerar som det ska så länge input är en siffra, men om man skriver t.ex. en bokstav blir det kaos. Vad beror detta på, och hur kan det lösas? Vad jag har förstått borde default-satsen exekveras om inputen inte matchar ett case.

Här är koden:

#include <stdio.h> int main () { int num1; int c; int avsluta; num1 = 0; avsluta = 0; while(avsluta==0){ printf ("Talet är %d, välj en opertion:\n", num1); printf ("1) Addera 1\n"); printf ("2) Multiplicera med 2\n"); printf ("3) Subtrahera 3\n"); printf ("4) Avsluta programmet\n"); scanf ("%d", &c); switch (c) { case 1: num1 = num1 + 1; break; case 2: num1 = num1 * 2; break; case 3: num1 = num1 - 3; break; case 4: printf ("Programmet avslutas."); avsluta=1; break; default: printf ("Felaktig operator\n"); break; }} return 0; }

Permalänk
Medlem

jag är ingen programmerare men jag tror att du har valt en variabel som är ett heltal. Det borde vara orsaken till att det blir galet med bokstäver/strängar

Visa signatur

.: Learn the system, Play the system, Break the system :.

Permalänk
Medlem

Jag har provat nu att byta ut operator till char ("1" istället för 1 osv.), nu går den till default som den ska. Tack!

Men.

Nytt problem: Programmet exekveras två gånger nu istället efter varje input... Andra gången går den direkt till default!

Permalänk
Medlem

Problemet är att scanf inte kan konvertera bokstaven till en int och låter indata vara kvar i en buffer och sen försöker konvertera det som är i bufferten, därför det blir en oändlig loop.
Testa att använda getline och sscanf istället.
Så datatypen behöver inte ändras bara hur du hanterar indata.

char *str = NULL; size_t n; getline(&str, &n, stdin); sscanf(str, "%d", &c);

https://stackoverflow.com/questions/25945791/character-input-...

Visa signatur

flippy @ Quakenet

Permalänk
Medlem

@Hazelberg: Som sagt, scanf opererar på en buffer och tar endast bort tecken från buffern om de matchar format-strängen. I fallet när du försöker plocka ut en siffra men matar in en bokstav så kommer scanf misslyckas och bokstaven kommer ligga kvar i buffern, vilket gör att scanf misslyckas även nästa gång, osv.

Orsaken till att programmet exekveras två gånger när du ändrar till char är för att indata läggs till i buffern när du trycker på enter, men detta lägger även till ett nyradstecken ('\n') i buffern. Så loopen kommer exekveras två gånger, första gången på tecknet du matade in och andra gången på nyradstecknet.

scanf returnerar antalet tecken som kunde läsas in, så du bör kontrollera att detta matchar antalet tecken du försökte läsa in (d.v.s. 1). Du bör också rensa buffern vid lämpliga tillfällen för att inte ha skräp som ligger kvar, t.ex. med while ((getchar()) != '\n'); som plockar bort tecken ur buffern till och med nästa nyradstecken. Eller som sagt använda getline för att läsa in en hel rad i taget.

Du får för övrigt gärna använda code-taggar när du postar kod, så blir det mycket lättare för oss att läsa. D.v.s.
[code]
kod här
[/code]

Permalänk
Medlem

@perost: Har suttit och pillat med detta i någon timme nu men får det inte att fungera. Menar du att getchar ska användas i stället för scanf, eller båda i kombination? Helt vilsen.

Det smidigaste skulle ju vara att använda scanf och göra så att programmet tar bort /n direkt. Jag försökte använda getchar-satsen med != '/n' som du skrev som input, men då behövdes två tecken skrivas in för att det skulle fungera... 11 blev 1, 21 blev 2, 1 blev inget osv.

*huvudvärk*

Permalänk
Medlem

@Hazelberg: getchar-satsen är till för att rensa buffern i förberedelse för nästa inmatning, inte för att läsa in indata.

Men jo, att läsa in indata kan vara rätt besvärligt i C om man vill få till det ordentligt. En lösning skulle vara att skippa scanf och bara använda getchar för att läsa in ett tecken i taget, och så ha ett case i switch-satsen som ignorerar '\n'-tecken.

Man kan också läsa in en hel rad till en buffer och utföra operationer på buffern istället för direkt på stdin, och sen bara slänga buffern när man är klar med den. För att läsa in en hel rad kan du använda getline, men då måste man hålla koll på minnet som används. Med getline kan man antingen allokera en buffer själv, eller låta getline göra det åt en. Oavsett så bör man frigöra minnet när man är klar med buffern, annars får man en minnesläcka. Så t.ex.:

// Om man vill att getline ska allokera minne åt en så ska buffer vara NULL och n vara 0. char *buffer = NULL; size_t n = 0; int c; while (...) { // Läs in en rad från stdin. getline(&buffer, &n, stdin); // Försök läsa in en siffra från buffern, och skriv ut felmeddelande om det inte gick. // sscanf returnerar antalet element som kunde läsas in, och vi frågar efter 1 element här. if (sscanf(buffer, "%d", &c) != 1) { printf("Kunde inte läsa in nummer\n"); } } free(buffer); // Frigör buffern.

Jag lade in while-loopen i exemplet för att visa att du kan återanvända buffern, och frigöra den efter loopen. Frigör du den i loopen så kommer det fortfarande fungera, men getline kommer allokera nytt minne varje gång. getline använder realloc för att utöka buffern om det behövs, så man behöver inte bry sig om storleken på den.