Föreläsning 8: Structar

Relevanta dokument
Föreläsning 6 pekare och pekare tillsammans med arrayer

HI1024, Programmering, grundkurs, 8hp KTH STH TENTAMEN. HI1024:TEN1 - Teoretisk tentamen Tid: Torsdagen den 20 oktober 2011,

Föreläsning 10. Pekare (Pointers)

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Programmering, grundkurs, 8.0 hp HI1024, omtentamen, TEN1. Tisdagen den 7 juni 2011,

Programmeringsteknik med C och Matlab

Agenda. Arrayer deklaration, åtkomst Makron Flerdimensionella arrayer Initiering Strängar Funktioner och arrayer. Övningar nu och då

Övning från förra gången: readword

Hantering av textsträngar och talsträngar. William Sandqvist

*Pekarvärden *Pekarvariabler & *

Planering Programmering grundkurs HI1024 HT 2014

Programmering, grundkurs, 8.0 hp, Elektro, KTH, hösten Programmering: att instruera en maskin att utföra en uppgift, kräver olika språk:

TDIU01 - Programmering i C++, grundkurs

HI1024, Programmering, grundkurs, 8hp KTH STH TENTAMEN. HI1024:TEN2 - Praktisk tentamen Tid: Fredagen den 21 oktober 2011,

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Programmering, grundkurs, 8.0 hp HI1024, TEN1. Fredagen den 2 mars 2012

En kort text om programmering i C.

Planering Programmering grundkurs HI1024 HT TIDAA

TDIU01 - Programmering i C++, grundkurs

Funktioner och programstruktur. Föreläsning 5

Planering Programmering grundkurs HI1024 HT data

Johan Karlsson Datavetenskap för teknisk kemi, 10p, moment 1 Datavetenskap Umeå Universitet. Tentamen

Föreläsning 2 Programmeringsteknik och C DD1316. Mikael Djurfeldt

Uppgifter till praktiska tentan, del A. (7 / 27)

Lösningar till uppgifterna sätts ut på kurssidan på nätet i dag kl Omtentamen i Programmering C, Fri, Kväll,

Programmering i C, 7,5 hp

Repetition C-programmering

KTH STH TENTAMEN. HI1024:TEN2 - Praktisk tentamen Tid: 8-13, den 18 februari 2012

Introduktion C-programmering

Funktioner och programstruktur. Föreläsning 5

Skriv i mån av plats dina lösningar direkt i tentamen. Skriv ditt kodnummer längst upp på varje blad.

HI1024 Programmering, grundkurs TEN

Övningsuppgifter till föreläsning 2 Variabler och uttryck

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Möte 7: Uppföljning av föreläsningen med Peer Instruction - (PI)

Grundläggande C-programmering del 2 Pekare och Arrayer. Ulf Assarsson

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

Föreläsning 10 Datalogi 1 DA2001. Utskrift på skärmen. Syntax. print( Hej ) Hur är det? Hej. print( Hej,end= ) print( Hur är det? ) HejHur är det?

HI1024 Programmering, grundkurs TEN

Programmering, grundkurs, 8.0 hp HI1024, extra tentamen, TEN1, för TIDAA1. Fredagen den 11 mars 2011,

Att använda pekare i. C-kod

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

Programmering Grundkurs (HI1900) Teoridel

Programmering, grundkurs, 8.0 hp, Elektro, KTH, hösten 2010

Grundkurs i programmering, 6 hp (725G61) Dugga 2 tillfälle 2

C++ - En introduktion

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

struct egendefinierad typ struct LECTURE_TYPE { char teacher[99]; float lengthinminutes; char type; /* L = lecture, E = exercise */ };

Föreläsning 2 Programmeringsteknik och C DD1316. Programmering. Programspråk

BMI = (vikt i kg) / (längd i m) 2. Lösningsförslag

Tentamen *:58/ID100V Programmering i C Exempel 3

Programmeringsteknik med C och Matlab

#include <stdio.h> #include <string.h>

Pekare och arrayer. Indexering och avreferering

DD1314 Programmeringsteknik

1 Funktioner och procedurell abstraktion

1/15/2013. DD1310/DD1314/DA3009 Programmeringsteknik. Lärandemål... Vilka läser kursen? ...fler lärandemål VARFÖR? Föreläsning 1

Tentamen i. för D1 m fl, även distanskursen. lördag 26 februari 2011

DD1310/DD1314/DA3009 Programmeringsteknik LÄRANDEMÅL... Vilka läser kursen? ...FLER LÄRANDEMÅL. Föreläsning 1

Föreläsning 5: Introduktion av pekare

Grundläggande C-programmering del 2 Pekare och Arrayer. Ulf Assarsson

Uppgiften är att beskriva en kvadrat i ett Java program. En första version av programmet skulle kunna se ut så här:

Om pekare och minneshantering i C, relaterat till operativsystem och särskilt konstruktionen fork() execvp().

Labora&on 2 Funk&oner, if och loop övningar/uppgi:er

Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

HI1024 Programmering, grundkurs TEN

Grundläggande datalogi - Övning 1

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

HI1024 Programmering, grundkurs TEN

Tentamen ges för: Tentamensdatum: Tid:

Föreläsning 2 Programmeringsteknik och Matlab DD1312. Programspråk. Utskrift på skärmen

Objektorienterad programmering i Java

Lösningar till uppgifterna sätts ut på kurssidan på nätet i dag kl Tentamen i Programmering C, Fri, Kväll,

Chapter 4: Writing Classes/ Att skriva egna klasser.

Alla datorprogram har en sak gemensam; alla processerar indata för att producera något slags resultat, utdata.

HI1024 Programmering, grundkurs TEN

Typkonvertering. Java versus C

Övningar Dag 2 En första klass

Föreläsning 2 Programmeringsteknik DD1310. Programmering. Programspråk

Lösningar till tentauppgifterna sätts ut på kurssidan på nätet idag kl

Tentamen i Introduktion till programmering

Programmering av inbyggda system. Kodningskonventioner. Viktor Kämpe

Imperativ programmering. Föreläsning 2

Kurslitteraturen. C-nivå Villkorssatser [kap 8] if & elif & else and & or Loopar [kap 9] for

2 Pekare och dynamiska variabler.

C++ Funktioner 1. int summa( int a, int b) //funktionshuvud { return a+b; //funktionskropp } Värmdö Gymnasium Programmering B ++ Datainstitutionen

Övningsuppgifter kapitel 8

Dynamiskt minne. Vad är dynamiskt minne Motivering Hur gör man i C Övningar

6.1 Kompilering och lite grundläggande information

Code-Lite tutorial ( /RoJ)

Fortsä'ning Pekare. Ulf Assarsson. Originalslides av Viktor Kämpe

TDIU01 - Programmering i C++, grundkurs

Möte 9: Relationer mellan körande processer - (PI)

Ulf Assarsson. Grundläggande C-programmering del 2 Pekare och Arrayer. Läromoment:

Introduktion till programmering SMD180. Föreläsning 2: Variabler, uttryck och satser

Java, klasser, objekt (Skansholm: Kapitel 2)

I Skapa Hej.java och skriv programmet. I Kompilera med javac Hej.java. I Rätta fel och repetera tills du lyckas kompilera ditt program

Lösningar till uppgifterna sätts ut på kurssidan på nätet idag kl Omtentamen i Programmering C, 5p, A1, D1, E1, Fri, Pr1, Te/Ek1,

Transkript:

Föreläsning 8: Structar Structar är ett sätt att få en variabel att innehålla mer än ett värde, precis som arrayer, men skillnaden, mellan structar och arrayer är att i en array så har alla element samma typ, exempelvis 10 heltal eller 10 tecken (som kansk kan lagra en sträng) eller 10 flyttal etc. En struct kan innehålla flera värden av olika typer, till exempel ett heltal, en teckenarray och ett flyttal. Boken går igenom 3 sätt att hantera structar, men vi ska bara gå igenom ett av dessa sätt, det enklaste sättet som alltid fungerar i alla sammanhang. Vi kommer att gå in i avsnitt 16.2 på en gång, 16.1 kan ni läsa översiktligt. 16.2 Typbegreppet för structar Normala variabler har en typ som vi anger då vi deklarerar dem, int x;, betyder till exempel att vi ska skapar en lagringsplats som vi benämner x och som lagrar ett heltal (int). När det gäller structar vill vi dock lagra olika typer av data i samma lagringsutrymme. För att åstadkomma detta anger vi först hur en struct ska se ut, vi anger en så kallad structure tag, som boken säger. På svenska kanske vi kan kalla det för struct-innehåll. Om vi till exempel vill lagra ett heltal och två strängar i en struct, för att göra en telefonbok till exempel, så skriver vi först ett struct-innehåll, så här: struct personuppgift int id; char namn[20]; char telefonnummer[20]; ; Då har vi beskrivit hur en struct ska se ut, här ett heltal och 2 arrayer. Vi kan nu göra deklarationen struct personuppgift person1, person2; och då får vi två variabler person1 och person2 som har innehållet id, namn och telefonnummer. Dessa delar av en struct kallas structens medlemmar och person1 och person2 är alltså två variabler som innehåller structar med medlemmarna id, namn och telefonnummer. Man kommer åt en medlem genom operatorn., alltså en punkt. Genom att skriva person1.id, person1.namn och person1.telefonnummer så kommer vi åt medlemmarna precis som vanliga variabler. Vi kan alltså göra så här: person1.id = 1; strcpy(person1.namn, Johnny ); strcpy(person1.telefonnummer, 087909473 ); så har vi lagt in mina kontaktuppgifter i variabeln person1. Den stora fördelen, som inte nog kan betonas, är att variabeln person1 nu samlar alla sammanhörande uppgifter. Vi är nu säkra på att Johnnys telefonnummer verkligen hänger ihop med Johnny. Operationer på structvariabler En till fördel med structar är att vi kan utföra operationer på dem som påverkar varje medlem. Ett mycket intressant exempel är att vi kan göra tilldelning, vi kan skriva person2 = person1; och då kommer alla uppgifter (id=1, namn= Johnny etc.) att kopieras över till person2. Vi behöver faktiskt inte kopiera alla structmedlemmar för sig, vilket kanske är en överaskning särskilt vad gäller strängmedlemmar som vi alltså inte behöver använda strcpy() för att kopiera. johnnyp@kth.se Sidan 1 av 11

Vi kan också skicka innehållet i en struct som parametrar till funktioner, vi kan till exempel ha nedanstående funktion och struct: struct part int number; char name[40]; int on_hand; ; void print_part (struct part p) printf( Part number: %d.\n, p.number); printf( Part name: %s.\n, p.name); printf( Quantity on hand: %d.\n, p.on_hand); ; On hand, på engelska betyder hur många som finns inne och detta kan användas för att beskriva ett lager som lagrar saker. Vi ska studera detta exempel i detalj i denna föreläsning. Om vi har en struct lagrad i variabeln part1 (vi har då tidigare gjort deklarationen struct part part1;) och gör följande anrop: part1.id = 528; strcpy(part1.name, Disk drive ); part1.on_hand = 10; så kan vi tolka detta som att vi lagrar en uppgift om att vi har en del som heter Disk drive, alltså diskettstation, den har identitetsnummer 528 och vi har 10 stycken hemma. Om vi nu gör anropet print_part(part1); så kommer det att resultera i utskriften Part number: 528. Part name: Disk drive. Quantity on hand: 10. Alltså en utskrift av de lageruppgifter som vi lagrat i de respektive medlemmarna. Alla medlemmar är förstås individuella för varje enskild struct, har vi två olika structar så kan de alltså lagra olika värden på sina respektive medlemmar. Vid ett funktionsanrop sker en kopiering så att innehållet i struct-variabeln part1 kopieras över i parametern som också är en struct. Denna kopiering tar en del kraft, särskilt om vi anropar en funktion säg flera miljoner gånger i sekunden. Det är därför många gånger mer effektivt att skicka en pekare till en struct om den ska vara parameter till en funktion. Ett alternativt sätt att deklarera funktionen ovan skulle då vara: void print_part (struct part *p) printf( Part number: %d.\n, p->number); printf( Part name: %s.\n, p->name); printf( Quantity on hand: %d.\n, p->on_hand); ; Vi har alltså deklarerat parametern som en pekare istället. Anropet måste nu ändras så att vi har adressoperatorn med, det nya anropet får då formen print_part(&part1);. 16.3 Arrayer och structar tillsammans Det vi vill göra med structar är att kombinera dem med arrayer. Ovan har vi sett exempel på hur vi kan ha en teckenarray som medlem i en struct, medlemmen char namn[20;] i det förra exemplet användes som en sträng och char name[40]; i det tidigare exemplet användes också som en sträng. Intressant är också att vi förstås kan bilda arrayer av structar så att vi kan lagra en hel johnnyp@kth.se Sidan 2 av 11

rad av objekt som vi alltså beskriver med en array av structar. En array av structen struct part låter oss då beskriva många parts alltså många delar. Vi har möjligheten att hantera ett helt register (också kallat databas) av delar. Det här är bakgrunden till ett av kursens viktigaste exempel som beskrivs i Mainatining a Parts Database. Tyvärr finns det ett problem med detta exempel och vi ska rätta till det i denna föreläsning och samtidigt illustrera en viktig aspekt av programmering. Exemplet är inte bra för det innehåller globala variabler vi ska undvika detta. Rent övergripande ser exemplet ut så här, boken kallar exemplet för inventory.c: inkluderingsdirektiv makron struct-innehållsdeklaration en inmatningsfunktion (read_line()) i en separat fil global variabel, en array av structar med registeruppgifter global variabel, antal rgisteruppgifter funktionsprototyper för att hantera den globala variabeln main() funktionsdefinitioner De olika funktionerna anropas sedan från main() och utför olika saker på den globala arrayen av delar och den globala variabeln som håller reda på antalet delar. Om vi till exempel ska mata in en ny del så anropas en funktion där användaren uppmanas att mata in de olika uppgifterna som ska definiera delen, dessa uppgifter läggs som medlemmar i en struct som läggs in i den globala arrayen av structar och så ökas den globala variabeln som räknar antalet delar med 1. Det här är kanske enkelt att förstå och det är också enkelt att själv modifiera programmet för att göra det bättre, bara att lägga till nya funktioner. Problemet är att inga av funktionerna har några parametrar, vilket innebär att de är beroende av globala variabler för att fungera, det är inte bra för då uppstår lätt fel i funktionerna. Om man låter en funktion vara beroende av globala variabler så vet vi inte så mycket när vi ser på funktionskoden vad den gör, den arbetar med en variabel, men den variabeln är inte deklarerad som parameter. Om den vore en parameter skulle det bli tydligare hur funktionen arbetar. Det här är någonting som man uppskattar som programmerare och vi ska sträva efter att ni får detta tidigt i kursen. När vi också har en global variabel som innehåller vårt register så är det svårt att arbeta med flera register, alla funktioner behandlar ju just bara den globala variabeln som innehåller registret. Om vi inte hade registret som en global variabel och istället skickade med en pekare till registret så skulle våra funktioner kunna riktas mot att hantera flera olika register samtidigt. Denna flexibilitet tappar vi med globala variabler. Man skulle då kunna tänka sig att vi skriver ett program som bara ska använda en global variabel så att det inte är ett problem, men då inför vi en svaghet. Vårt program kan då inte lätt utvidgas, det skulle vara mycket lättare att utvidga programmet om vi inte hade globala variabler. Då kanske man tänker att det spelar ingen roll eftersom jag aldrig i livet kommer att vilja utvidga detta program ändå. Det är ändå ett problem, för det visar sig faktiskt att man ofta behöver underhålla program, alltså ändra i dem för att göra dem bättre eller bara rätta fel. Det är faktiskt extra svårt att ens rätta ett program som inte är skrivet så att det är lätt att utvidga det, så även om vi inte ska utvidga eller förbättra ett program är det alltid bra att skriva ett program som om vi skulle utvidga och förbättra det i framtiden. Vår strategi är därför att skriva program som inte har några globala variabler och deklarera alla variabler i main() och till varje funktion skicka pekare till variablerna som parametrar till funktionerna som ska behandla registret. Detta är en strategi som vi ska ha hela tiden. johnnyp@kth.se Sidan 3 av 11

Vi ändrar i programmet i boken så att det uppfyller de krav vi beskrivit ovan. Eftersom vi inte ännu gått igenom hur vi lägger delar av ett program i separata filer lägger vi också in hela programmet i en fil som heter database.c. Vi använder ett annat namn än inventory.c för att kunna skilja dem åt. Så här ser database.c ut: inkluderingsdirektiv makron struct-innehållsdeklaration funktionsprototyper för att hantera registeruppgifter, skickas i parametrarna funktionsprotoypen för inmatningsfunktion (read_line()) main() med lokala variabler: en array av structar md registeruppgifter och en heltalsvariabel som räknar antal rgisteruppgifter funktionsdefinitioner inklusive den för read_line(). Att vi lägger in read_line() i programmet är inte en viktig förändring, det gör vi bara för att ni lättare ska förstå detta program. Det absolut viktigaste och centrala här är att vi ändrar i hanteringen av de data som programmet använder: i bokens version, inventory.c, är data hanterat via globala variabler. Vi flyttar in deklarationerna av dessa variabler i main() och de blir då lokala. Detta är mycket viktigt, om vi kan undvika att arbeta med globala variabler ska vi göra det. För att vi ska kunna koppla ihop funktionerna med de data som de ska hantera skickar vi alltså dem som parametrar, vidare skickar vi ofta adresser till variablerna. Vi ser på ett par centrala exempel ur database.c, alltså inte bokens version, vi studerar funktionen insert(): /*insert: Prompts the user for information about a new part and the inserts the part into the database. Prints an error message and returns prematurely if the part already exists or if the database is full.*/ void insert(struct part *parts, int *num_parts) int part_number; if(*num_parts == MAX_PARTS) printf("database is full; can't add more parts.\n"); return; printf("enter part number: "); scanf("%d", &part_number); if (find_part(part_number, parts, *num_parts) >= 0) printf("part already exists.\n"); return; parts[*num_parts].number = part_number; printf("enter part name: "); read_line(parts[*num_parts].name, NAME_LEN); printf("enter quantity on hand: "); scanf("%d", &parts[*num_parts].on_hand); (*num_parts)++; johnnyp@kth.se Sidan 4 av 11

Parameterlistan är struct part *parts, int *num_parts och det betyder att funktionen tar emot en adress till en variabel av typen struct part samt en adress till ett heltal, int *num_parts. Vi använder första parametern för att skicka en array av structar, minns att arrayer kan skickas i form av adresser och den andra variabeln används för att öka variabeln som håller reda på antalet delar som programmet hanterar. Vi kan se det allra sist i koden, satsen (*num_parts)++; betyder att det som num_parts pekar på ökas med 1 och main(), som anropar funktionen, skickar med adressen till variabeln som håller reda på antalet delar som andra argument. Minns att main() också har en jättearray av structar som alltså (som nämnt ovan) skickas som första parameter. Anropet i main() ser ut så här: insert(parts1,&num_parts1); och sker i respons till att användaren väljer kommandot i, för insert. Vi jämför nu bokens inventory.c med database.c, skillnaderna illustrera många viktiga begrepp ganska väl och detta är ett av kursens centrala moment. Det är definitivt värt att lägga ner mycket tid på detta avsnitt. Vi studerar den första skillnaden: Bokens variant, inventory.c If (num_parts == MAX_PARTS) Vår variant, database.c If (*num_parts == MAX_PARTS) Kodmässigt är skillnaden är bara värdeoperatorn, alltså stjärnan (*). Inte så mycket kan tänkas, men det är en väsentlig skillnad, bokens funktion, insert(), har inga parametrar utan referenser till num_parts är en referens till en global variabel som gäller i hela programmet. I vår variant skriver vi *num_parts och refererar då till det som skickas till funktionen. Vår funktion tar emot en pekare då till antal argument som vi bestämmer i main(), och här skickar vi med adressen till en lokalt deklarerad variabel som heter num_parts1. Detta illustrerar flera saker. För det första har vi en bättre överblick över situationen i vår funktion, allt som den hanterar skickas med parametrarna. Vi har sämre överblick över hur bokens funktion eftersom den refererar till en global variabel num_parts. Detta kan tyckas som petitesser men är faktiskt av mycket stor betydelse då vi får större program. Det här att skicka allt i parametrar gör att funktionen (och dess konsekvenser) blir koncentrerad på en skärmsida, vi behöver inte scrolla upp för att kolla vilken global variabel som betyder vad. Nästa skillnad är av enklare natur, vi refererar till *num_parts igen, i en if-sats, av precis samma skäl som ovan, så vi avstår från att kommentera det. Vi går istället vidare till nästa skillnad som är mer intressant: Bokens variant, inventory.c inventory[num_parts].number=part_number; Vår variant, database.c parts[*num_parts].number = part_number; Återigen verkar skillnaden vara liten, bara en värdeoperator (*) i vår variant och inte en värdeoperator hos boken. Visserligen har vi ett annat variabelnamn, parts, och boken har namnet inventory, men det är ju bara skillnad i variabelnamnen, det är samma typ av sats, eller? En enkel tilldelning... Är det så stor skillnad? Ja, återigen är det en väldigt stor skillnad. I bokens kod har vi en global variabel, inventory, som lagrar alla delar som finns i registret. Vår funktion refererar till parametern parts och det bestäms i main() vart den ska referera. Parameterns deklaration är en pekar till structar och då kan vi skicka adressen till arrayen av structar som är deklarerad i huvudprogrammet. Faktiskt är parameterns namn, parts, valt med viss omsorg. Bokens program kan bara hantera ett register, eftersom varje funktion refererar till fixa globala variabler johnnyp@kth.se Sidan 5 av 11

inventory och num_parts men våra funktioner behandlar det de får i sina parametrar och inget annat. Det finns då en möjlighet att main() kan hantera flera olika register med dessa funktioner genom att anropa samma funktioner a. Vi illustrerar det så genom att faktiskt deklarera två uppsättningar variabler som potentiellt kan hantera två uppsättningar register, i main() har vi deklarationerna: struct part parts1[100]; int num_parts1 = 0; /* struct part parts2[100]; int num_parts2 = 0; */ men vi använder inte variablerna parts2 och num_parts2, de är till och med bortkommenterade. De finns bara där för att illustrera en möjlighet som vi har då vi använder parametrar till funktioner istället för globala variabler. Jag tyckte att det då var naturligt att sätta parameternamnet parts istället för inventory eftersom ordet inventory på engelska betyder allt som finns inne. Vårt program kan hantera olika uppsättningar av saker (i och med att funktionerna inte är hårt knutna till fixa globala variabler som i bokens exempel) och då tänkte jag att det är bättre att kalla parametern för parts, det ger en liten vink om att vi hanterar inte alla saker, bara det som skickas i parametern. Det här kan tyckas som subtila och svårbegripliga aspekter i början, men jag rekommenderar er att faktiskt ägna mycket tid åt detta exempel. Det är kärnan i att skriva bra strukturerade program och det är en viktig förmåga som ni absolut måste tillägna er. De övriga skillnaderna i insert() är baserade på precis samma motiveringar som vi gått igenom ovan så vi avstår från upprepande kommentarer kring dem. Vi kan dock jämföra två av funktionerna. Vi studerar funktionsprototyperna för search() och insert(): void insert (struct part *parts, int *num_parts); void search (struct part *parts, int num_parts); Vi ser att vi deklarerar num_part som pekare till heltal i insert() men som vanligt heltal i search(). Varför det? Jo, i insert() vill vi öka antalet på antalet registeruppgifter, vi vill också att insert() själv ska avgöra om antalet registeruppgifter ska ökas eller ej (varför det?), därför måste insert() få hantera den variabel som innehåller antal registeruppgifter, därför måste den ha adressen till den variabeln som parameter, inte bara en kopia av värdet. Om allt gått bra i insert() ser vi också på slutet att värdet ökas genom satsen (*num_parts)++;. Till skillnad från detta ser vi att search() inte har num_parts deklarerad som adress till int, anledningen är då att search() absolut säkert inte ska ändra på värdet på variabeln som innehåller antalet registeruppgifter. Därför skickar vi inte den som adress till heltal, här räcker det med att få en kopia av hur många registeruppgifter som det finns. Nu kommer då frågan, men det finns ingen skillnad i deklarationerna av den första parameter, parts, den är deklarerad som pekare till struct part i båda fallen. Hur stämmer det? Jo, eftersom vi skickar en array i parts-parametern (man kan ju göra så med pekare, arraynamn är ju också pekare) så måste vi pekare i båda fallen, även om vi inte ändrar på innehållet i search(). johnnyp@kth.se Sidan 6 av 11

Vi studerar en provköning: HI1024 Programmering, grundkurs, 8.0 hp, KTH STH, hösten 2012 $./database Enter operation code (i/s/u/p/q): i Enter part number: 1 Enter part name: Disk drive Enter quantity on hand: 4 Illegal code. Part number Part Name Quantity on Hand 1 Disk drive 4 Enter operation code (i/s/u/p/q): i Enter part number: 4 Enter part name: Printer cable Enter quantity on hand: 19 Illegal code. Part number Part Name Quantity on Hand 1 Disk drive 4 4 Printer cable 19 Enter operation code (i/s/u/p/q): s Enter part number: 4 Part name: Printer cable. Quantity on hand: 19. Enter operation code (i/s/u/p/q): u Illegal code. Enter operation code (i/s/u/p/q): u Enter part number: 1 Enter change in quantity on hand: 10 Illegal code. Part number Part Name Quantity on Hand 1 Disk drive 14 4 Printer cable 19 Enter operation code (i/s/u/p/q): johnnyp@kth.se Sidan 7 av 11

Körningen är inte helt stabil, vi ser meddelandet Illegal code ett par gånger trots att vi visst skriver in giltiga operationskoder. Jag har inte helt genomskådat varför det är så här, mitt råd till er är att vi egentligen inte ska arbeta med konsolprogam på det här sättet på sikt, på sikt ska vi givetvis lära oss att skapa fönsterbaserade användargränssnitt (GUI) så vi behöver inte fördjupa oss för mycket i varför vi hamnar i otakt då och då. Problemet uppstår när vi blandar inmatning av heltal och strängar, detta exempel visar en lösning av det problemet. När vi studerar kapitlet om stora program ska vi se på en annan lösning. Vi ger nedan hela källkoden till programmet database.c, det är självfallet rekommenderat att ni provkör detta program. #include <stdio.h> #define NAME_LEN 25 #define MAX_PARTS 100 struct part int number; char name[name_len+1]; int on_hand; ; int find_part (int number, struct part *parts, int num_parts); void insert (struct part *parts, int *num_parts); void search (struct part *parts, int num_parts); void update (struct part *parts, int num_parts); void print (struct part *parts, int num_parts); int read_line(char str[], int n); /* main: Prompts user to enter operation code, then calls the appropriate function to perform the command. Repeats until user enters 'q', to quit. */ main() char code, buf[2]; struct part parts1[100]; int num_parts1 = 0; /* struct part parts2[100]; int num_parts2 = 0; */ for(;;) printf("enter operation code (i/s/u/p/q): "); scanf("%c", &code); while (getchar()!='\n') /* Skips to end of line*/ ; johnnyp@kth.se Sidan 8 av 11

HI1024 Programmering, grundkurs, 8.0 hp, KTH STH, hösten 2012 switch(code) case 'i': insert(parts1,&num_parts1); break; case 's': search(parts1,num_parts1); break; case 'u': update(parts1,num_parts1); break; case 'p': print(parts1,num_parts1); break; case 'q': return 0; default: printf("illegal code.\n"); printf("\n"); /*find_part: Looks up a part number in the inventory array Returns the array index if the part with the part number is found. Returns -1 otherwise.*/ int find_part(int number, struct part *parts, int num_parts) int i; for(i=0;i<num_parts;i++) if(parts[i].number == number) return i; return -1; /*insert: Prompts the user for information about a new part and the inserts the part into the database. Prints an error message and returns prematurely if the part already exists or if the database is full.*/ void insert(struct part *parts, int *num_parts) int part_number; if(*num_parts == MAX_PARTS) printf("database is full; can't add more parts.\n"); return; printf("enter part number: "); scanf("%d", &part_number); if (find_part(part_number, parts, *num_parts) >= 0) printf("part already exists.\n"); return; parts[*num_parts].number = part_number; printf("enter part name: "); read_line(parts[*num_parts].name, NAME_LEN); printf("enter quantity on hand: "); scanf("%d", &parts[*num_parts].on_hand); (*num_parts)++; johnnyp@kth.se Sidan 9 av 11

/*search: Prompts the user to enter a part number, then looks up the part in the database. If the part exists, prints the name and quantity on hand; if not, prints an error message.*/ void search(struct part *parts, int num_parts) int i, number; printf("enter part number: "); scanf("%d", &number); i = find_part(number, parts, num_parts); if(i>=0) printf("part name: %s.\n", parts[i].name); printf("quantity on hand: %d.\n", parts[i].on_hand); else printf("part not found.\n"); /*update: Prompts the user to enter a part number. Prints an error message if the part doesn't exist; otherwise, prompts the user to enter change in quantity on hand and updates the database.*/ void update(struct part *parts, int num_parts) int i, number, change; printf("enter part number: "); scanf("%d", &number); i = find_part(number, parts, num_parts); if(i>=0) printf("enter change in quantity on hand: "); scanf("%d", &change); parts[i].on_hand += change; else printf("part not found.\n"); johnnyp@kth.se Sidan 10 av 11

/*print: Prints a listing of all parts in the database, showing th part number, part name, and quantity on hand. Parts are printed in the order in which they were entered into the database.*/ void print (struct part *parts, int num_parts) int i; printf("part number Part Name " "Quantity on Hand\n"); for(i=0;i<num_parts;i++) printf("%7d %-25s%11d\n", parts[i].number, parts[i].name, parts[i].on_hand); /*read_line: Skips leading white-space characters, then reads the remainder of the input linee and stores it in str. Truncates the line if the input exceeds n. Returns the number of characters stored.*/ int read_line(char str[], int n) int ch, i = 0; while (isspace(ch=getchar())) ; while (ch!= '\n' && ch!= EOF) if (i < n) str[i++] = ch; ch = getchar(); str[i] = '\0'; return i; johnnyp@kth.se Sidan 11 av 11