Inmatningsproblemet
Då man matar in data med scanf() till ett C-program kan det ibland uppstå problem när man gör
inmatningar flera gånger i följd. Man kan då uppleva att programmet hoppar över en inmatning. Jag
ska illustrera en lösning till detta problem som inte är en professionell lösning, men som i alla fall
fungerar.
Problematiken uppstår på grund av att det finns en inmatningsbuffert där tangenttryckningar lagras.
Då vi gör en inmatning med scanf() matchas formatsträngen i anropet till scanf() mot
innehållet i bufferten och inläsning sker. Om bufferten innehåller white space, alltså mellanslag,
tabulatorsteg eller nyradstecken behandlas dessa såsom formatsträngen anger. Vid inläsning av ett
heltal med formatsträngen “%d” läses de tangentryckningar som motsvarar ett heltal in och rätt
variabel uppdateras. Vid upprepade inläsningar av heltal verkar C vara stabilt, följande program,
som bara läser in två heltal och skriver ut dem efter varandra fungerar bra i NewTinyDebian:
#include <stdio.h>
main()
{
int t1, t2;
printf("Tal 1: "); scanf("%d", &t1);
printf("Tal 1: %d\n", t1);
printf("Tal 2: "); scanf("%d", &t2);
printf("Tal 2: %d\n", t2);
}
En provkörning:
Tal
Tal
Tal
Tal
1:
1:
2:
2:
10
10
20
20
Här är det som användaren matar in angivet i fetstil. Programmet skriver alltså bara ut det som
användaren matat in och det uppstår inga problem. Om vi däremot byter datatyp till teckentypen,
char, och arbetar med motsvarande program för tecken:
#include <stdio.h>
main()
{
char ch1, ch2;
printf("Tecken 1: "); scanf("%c", &ch1);
printf("Tecken 1: %c\n", ch1);
printf("Tecken 2: "); scanf("%c", &ch2);
printf("Tecken 2: %c\n", ch2);
}
så får vi faktiskt problem. En provkörning visar att beteendet i programmet verkar vara det att helt
enkelt hoppa över andra inmatningen:
Tecken 1: A
Tecken 1: A
Tecken 2: Tecken 2:
Det här kan kännas väldigt frustrerande, men det finns en enkel förklaring och en enkel lösning.
Bakgrunden är den så kallade inmatningsbufferten och vi kan visualisera programmets beteende
genom att tänka oss inmatningsbufferten som en kö till programmet. Denna kö innehåller
tangenttryckningar och när vi vill mata in två tecken till programmet ovan så vill vi trycka
tangenterna 'A', 'returtecken', 'B', 'returtecken' och tänka oss att programmet läser in dessa. Dock så
verkar som sagt programmet ovan inte ens ge oss en chans att skriva in 'B'. Förklaringen ligger i
scanf():s beteende. Formatsträngen “%c” anger att vi ska läsa in ett tecken i variabeln vars
adress följer efter formatsträngen. Av någon anledning väljer scanf() att endast läsa
tangenttryckningen 'A' (i det här exemplet) och lämna kvar returtangenttryckningen i kön. När
sedan nästa scanf() kommer och försöker läsa så kan inte den scanf() bortse från
returtangenttryckningen utan läser in den och uppfattar den som ett slags slut på inmatning.
Lösningen är att tömma inmatningsbufferten på returtangenttryckningar mellan anropen till
scanf(). Det kan vi göra genom att läsa en så kallad sträng istället. Vi har inte börjat med
strängar ännu, men vi kan ge en specialkonstruktion för att komma tillrätta med detta problem. Vi
skriver om programmet ovan, så här:
#include <stdio.h>
#include <string.h>
main()
{
char ch1, ch2;
char buf[2];
printf("Tecken 1: "); scanf("%c", &ch1); gets(buf);
printf("Tecken 1: %c\n", ch1);
printf("Tecken 2: "); scanf("%c", &ch2); gets(buf);
printf("Tecken 2: %c\n", ch2);
}
Och nu fungerar programmet bra, en testkörning visas nedan:
Tecken
Tecken
Tecken
Tecken
1:
1:
2:
2:
A
A
B
B
Vi får en varning vid kompileringen men vi kan ignorera den nu i början av kursen. Vi ska se senare
hur man rättar till det. Vad vi gör är att vi anropar funktionen gets() som läser en hel rad från
inmatningsbufferten, fram till nästa nyradstecken, vi läser in detta in strängen buf som är
deklarerad som en array av tecken. Vi behöver inte veta vad detta är ännu, vi kan bara lägga på ett
gets(buf) direkt efter varje scanf() så fungerar de program vi skriver som vi tänkt. Senare
ska vi se i detalj på vad gets() och vad strängar egentligen är, men just nu kan vi göra så här för
att kunna komma vidare i de situationer då inmatningen inte fungerar som vi tänkt.