DSV Jozef Swiatycki Tentamen *:58/ID100V Sid 1(5) Exempel 3 Tentamen *:58/ID100V Programmering i C Exempel 3 Denna tentamen består av fyra uppgifter som tillsammans kan de ge maximalt 22 poäng. För godkänt resultat krävs minst 14 poäng , för VG krävs minst 18 poäng. För KTH-studenter tillämpas A/B/C/D/E/F-skalan enligt betygskriterier på kursens webbsida. Observera: - skriv tydligt, oläsbar lösning medför 0 poäng - börja varje uppgift på nytt blad - skriv endast på ena sidan av pappret - skriv namn på varje inlämnat blad - redovisa eventuella antaganden (som givetvis inte får strida mot uppgiftstexten) - tänk på att det kan passa dig bättre att lösa uppgifterna i annan ordning Tillåtna hjälpmedel: - valfria medhavda böcker om programmering i C - medhavt häfte med kopior av föreläsningsbilder LYCKA TILL ! DSV Jozef Swiatycki Tentamen *:58/ID100V Sid 2 Exempel 3 Uppgift 1 (Sammanlagt 6 poäng) Denna uppgift består av tre deluppgifter som vardera ger två poäng. Varje uppgift består av en liten C-programsnutt behäftat med ett fel samt en beskrivning av programsnuttens felaktiga beteende. Din uppgift är att I) ange vad felet är och II) ange hur felet skall rättas. Obs! att du för att få poängen skall rätta just detta fel, en omskrivning av programmet med andra konstruktioner duger inte. Obs! vidare att det i vissa fall kan krävas små ändringar på flera ställen i programsnutten för att rätta ett fel. Samtliga fel är av "klassisk" karaktär, som det varnats för i undervisningen och/eller i litteraturen. Det är inte fråga om syntaxfel, utan om felaktigheter som gör att programmet inte beter sig som det skall eller får exekveringsavbrott. a) Följande funktion är tänkt att kopiera en fil till en annan: #include <stdio.h> void copyfile(FILE *inf, FILE *outf){ char c; while ((c=getc(inf))!=EOF) putc(c, outf); } Funktionen har testats på flera system, på vissa går den in i en oändlig loop, på andra slutar den för tidigt vid kopiering av binärfiler. b) I följande program verkar det vara något fel på funktionen geVarden(). Funktionen skall fylla posten den får som argument med värden som den också får som argument, men den verkar inte åstadkomma någon förändring typedef struct _post{ char *namn; int data; } Post; void geVarden(Post p, char *n, int d){ p.namn=n; p.data=d; } int main(void){ Post pst; geVarden(pst, "Jozef", 53); printf("%s %d\n", pst.namn, pst.data); return 0; } Deluppgift c) på nästa sida! DSV Jozef Swiatycki c) Tentamen *:58/ID100V Sid 3 Exempel 3 Funktionen getstr() nedan skall användas för inläsning av strängar från stdin. Satserna i funktionen följer ett välkänt mönster och är inte felaktiga. Funktionen verkar fungera i mycket små testprogram, men vid användning i lite mer komplexa program verkar den inlästa strängen blir förstörd på något sätt. #include <stdio.h> #include <string.h> char *getstr(int max){ char buff[100]; char *cpek; fgets(buff, max, stdin); if (cpek=strchr(buff, '\n')) *cpek='\0'; else while(getchar()!='\n') ; return buff; } Uppgift 2 (Variabla argumentlistor - 2 poäng) Skriv funktionen char *strconc(char *dest, ...); som kan konkatenera ett godtyckligt antal strängar som den får som argument. Det sista argumentvärdet vid ett anrop till strconc skall vara NULL för att markera att argumenlistan är slut. Strängarna skall konkateneras i utrymmet dest. Det är anroparens ansvar att se till att dest kan rymma alla dessa konkatenerade strängar. strconc skall returnera dest. Exempel på anrop: strconc(buff, "Detta ", "var ", "lätt", NULL); Efter detta anrop skall buff innehålla strängen "Detta var lätt". Uppgift 3 (Otypat minne - 2 poäng) Skriv funktionen void *memmem(void *ptr1, void *ptr2, size_t n1, size_t n2); Denna funktion skall fungera på godtyckliga minnesareor på samma sätt som funktionen strstr() fungerar på strängar: den skall gå igenom arean som pekas ut av ptr1 och är n1 bytes stor och den skall söka där efter en byteföljd som är lika med den byteföljd som finns i arean som pekas ut av ptr2 och är n2 bytes stor. memmem() skall returnera en pekare till den första påträffade förekomsten av denna byteföljd, eller NULL om ingen sådan förekomst påträffas. DSV Jozef Swiatycki Tentamen *:58/ID100V Sid 4 Exempel 3 Uppgift 4 (Generella moduler, dynamiska arrayer, funktionspekare - 12 poäng) Denna uppgift handlar om att skriva (delar av) en återanvändbar modul som skall definiera datastrukturen Arrayset för representation av mängder av element av godtyckliga typer, samt visa vad en tillämpning behöver göra för att kunna använda modulen. Till skillnad från inlämningsuppgift 3 skall denna datastruktur kunna hantera godtyckliga data. Detta innebär att den bör implementeras med pekare till de data som stoppas in i mängden. En mängd är ju en datastruktur där det inte förekommer dubletter. T.ex. ska funktionen add() nedan inte förändra mängden om det i mängden redan finns ett element som är lika med det nya element som tillämpningen försöker addera. Men eftersom modulen ska kunna hantera element av godtyckliga typer så är det upp till tillämpningen att bestämma vad ”lika med” betyder för dess objekt. Internt ska Arrayset (som själva namnet visar) vara implementerad som en omallokerbar array av pekare till tillämpningsobjekt. Du behöver inte ta hänsyn till några effektivitetsaspekter. a) Skriv modulen arrayset.c. Modulen beskrivs av nedanstående ofullständiga headerfil arrayset.h, där argumenten till funktionerna initset() och removeset() saknas - du ska själv bestämma vilka de ska vara. #ifndef ARRAYSET #define ARRAYSET typedef struct arrstruct *Arrayset; Arrayset initset(???????????); int addset(Arrayset s, void *new); void removeset(Arrayset s, ???????????); #endif • initset() ska skapa, initiera och returnera en Arrayset • addset() ska stoppa in elementet new i Arrayset:et s om det inte redan finns ett element som är ”lika med” new i s. Returnerar 1 vid lyckad operation, 0 annars. • removeset() ska ta bort från Arrayset:et s alla element som uppfyller något villkor som tillämpningen anger. Du måste alltså komma på ett sätt för en tillämpning att ange ett villkor så att removeset() kan testa om elementen uppfyller villkoret. Du måste också se till att man kan förhindra att minnesläckage uppstår vid denna operation. Titta gärna på uppgift b) när du bestämmer hur argumenten till removeset ()ska se ut. Uppgift b) finns på nästa sida! DSV Jozef Swiatycki Tentamen *:58/ID100V Sid 5 Exempel 3 b) Antag en tillämpning där man hanterar namn mha följande struct: typedef struct name{ char *firstname; char *lastname; } Name; Objekt av denna typ skapas med följande funktion: Name *makename(char *fn, char *ln){ Name *tmp = malloc(sizeof(Name)); tmp->firstname = malloc(strlen(fn)+1); strcpy(tmp->firstname, fn); tmp->lastname = malloc(strlen(ln)+1); strcpy(tmp->lastname, ln); return tmp; } /* makename */ Två Name-objekt är lika om resp. firstname och lastname i båda har samma strängvärde. Komplettera följande program med vad som behövs för att det ska kunna använda Arrayset på sina namn : #include ”arrayset.h” int main(void){ Arrayset namn = initset(???????????); addset(namn, makename("john", "grisham")); addset(namn, makename("ken", "follet")); addset(namn, makename("mike", "palmer")); addset(namn, makename("james", "follet")); addset(namn, makename("mike", "ridpath")); removeset(namn, ???? /* de som heter "john" i förnamn */); removeset(namn, ???? /* de som heter "mike" i förnamn */); return 0; }