Programexempel från Progmet-föreläsning 1, tisdag 4 september 2012 ================================================================== Planering för idag: Separatkompilering: .c-filer; definition och deklaration Modularisering och abstraktion: abstrakt datatyp, .h-fil Abstrakt datatyp: Person (repetition av struct och struct-funktioner; abstraktionen) Abstrakt datatyp: Komplexa tal (repetion av matematiken; abstraktionen) Om vi hinner (fet chans...): Att hantera flera värden (double, heltal, poster...): fil Parametrar till main Att hantera flera värden (double, heltal, poster...): array (repetion) Pekare (repetition) malloc och free av en enda "variabel" (double, heltal, post) malloc och free av array Länkad lista: "invasiv enkellänkad lista" (double, heltal, post) Exempel 1: Alltihop i en enda fil --------------------------------- personmain-1.c -------------- #include struct Person { int nr; char namn[35 + 1]; }; void skriv_person(struct Person p) { printf("Nr: %d\n", p.nr); printf("Namn: %s\n", p.namn); } int main(void) { struct Person personen; skriv_person(personen); return 0; } Övning: Hur ser utmatningen ut? Övning: Lägg till las_person, mindre_an (jämförelse), två personer Exempel 2: Uppdelat på två filer (person.c och personmain-2.c) -------------------------------------------------------------- person.c -------- #include struct Person { int nr; char namn[35 + 1]; }; void skriv_person(struct Person p) { printf("Nr: %d\n", p.nr); printf("Namn: %s\n", p.namn); } personmain-2.c -------------- struct Person { int nr; char namn[35 + 1]; }; void skriv_person(struct Person p); int main(void) { struct Person personen; skriv_person(personen); return 0; } Exempel 3: Uppdelat på två filer, med include-fil ------------------------------------------------- Person.h -------- struct Person { int nr; char namn[35 + 1]; }; void skriv_person(struct Person p); Person.c -------- #include #include "Person.h" void skriv_person(struct Person p) { printf("Nr: %d\n", p.nr); printf("Namn: %s\n", p.namn); } personmain-3.c -------------- #include "Person.h" int main(void) { struct Person personen; skriv_person(personen); return 0; } Ett exempel till på moduler och .h-filer ---------------------------------------- main --> Skolklass --> Person | | | | | V +--------> stdio Filer: Person.c, Person.h, Skolklass.c, SKolklass.h, main.c, inte main.h Person.h = som förut Person.c = som förut Skolklass.h ----------- struct Skolklass { struct Person elever[30]; }; void skriv_skolklass(struct Skolklass s); Skolklass.c ----------- #include #include "Person.h" #include "Skolklass.h" void skriv_skolklass(struct Skolklass s) { for (int i = 0; i < 30; ++i) skriv_person(s.elever[i]); } skolklassmain.c --------------- #include "Skolklass.h" int main(void) { struct Skolklass ek3; skriv_skolklass(ek3); return 0; } Ger kompileringsfel vid kompilering av skolklassmain.c ------------------------------------------------------ Om man expanderar preprocessor-direktiven i skolklassmain.c genom att stoppa in Skolklass.h där det står "#include": struct Skolklass { struct Person elever[30]; <-- Kompilatorn säger: Va? Vad är "Person" för nåt? }; struct skriv_skolklass(struct Skolklass s); int main(void) { struct Skolklass ek3; skriv_skolklass(ek3); return 0; } Lösning (nästan): Inkludera Person.h även i Skolklass.h! Ny Skolklass.h -------------- #include "Person.h" struct Skolklass { struct Person elever[30]; }; struct skriv_skolklass(struct Skolklass s); Men nu får vi kompileringsfel vid kompilering av Skolklass.c ------------------------------------------------------------ "redefinition of 'struct Person'" -- eftersom Person.h nu inkluderas TVÅ gånger, en gång direkt och en gång indirekt via Skolklass.h Lösning: Inför "inkluderingsskydd" i Person.h: Ny Person.h ----------- #ifndef PERSON_H_INCLUDED #define PERSON_H_INCLUDED struct Person { int nr; char namn[35 + 1]; }; void skriv_person(struct Person p); #endif Abstraktion: att gömma undan (eller i alla fall slippa bry sig om) detaljer --------------------------------------------------------------------------- Exempel 1: Heltal är en sorts abstraktion ----------------------------------------- abstraktion-1.c --------------- #include #include int main(void) { // Abstraktion: heltal int x; x = 19; if (x == 19) printf("Talet var 19.\n"); // Under abstraktionen: bitar printf("Antal bytes i en int: %d\n", (int)sizeof(int)); printf("Antal bitar i en int: %d\n", (int)sizeof(int) * CHAR_BIT); unsigned char *cp = (unsigned char*)&x; printf("Bitmönster (hexadecimalt): "); for (int i = 0; i < sizeof(int); ++i) { printf("%02x", cp[i]); } printf("\n"); return 0; } Utmatning --------- Talet var 19. Antal bytes i en int: 4 Antal bitar i en int: 32 Bitmönster (hexadecimalt): 13000000 Abstrakt datatyp ---------------- "Abstrakt datatyp" = datatyp ("en samling värden") med tillhörande funktioner Exempel: struct Person, skriv_person, ... Jämför med: int, +, -, ... Exempel på abstraktion: Komplexa tal ------------------------------------ abstraktion-3.c --------------- #include #include struct Complex { double re; double im; }; typedef struct Complex Complex; Complex make_complex(double re, double im) { Complex z; z.re = re; z.im = im; return z; } double real_part(Complex z) { return z.re; } double imaginary_part(Complex z) { return z.im; } double magnitude(Complex z) { return sqrt(z.re * z.re + z.im * z.im); } double angle(Complex z) { return atan(z.im / z.re); } void print_complex(Complex z) { if (imaginary_part(z) < 0) printf("%f - i%f", real_part(z), -imaginary_part(z)); else printf("%f + i%f", real_part(z), imaginary_part(z)); } Complex add_complex(Complex c1, Complex c2) { return make_complex(real_part(c1) + real_part(c2), imaginary_part(c1) + imaginary_part(c2)); } int main(void) { Complex z1 = make_complex(1, 0); Complex z2 = make_complex(0, 1); Complex z3 = add_complex(z1, z2); printf("z1 = "); print_complex(z1); printf("\n"); printf("magnitude(z1) = %f\n", magnitude(z1)); printf("z2 = "); print_complex(z2); printf("\n"); printf("magnitude(z2) = %f\n", magnitude(z2)); printf("z3 = "); print_complex(z3); printf("\n"); printf("magnitude(z3) = %f\n", magnitude(z3)); return 0; } Utmatning --------- z1 = 1.000000 + i0.000000 magnitude(z1) = 1.000000 z2 = 0.000000 + i1.000000 magnitude(z2) = 1.000000 z3 = 1.000000 + i1.000000 magnitude(z3) = 1.414214 abstraktion-4.c --------------- #include #include struct Complex { double magnitude; double angle; }; typedef struct Complex Complex; Complex make_complex(double re, double im) { Complex z; z.magnitude = sqrt(re * re + im * im); z.angle = atan(im / re); return z; } double real_part(Complex z) { return z.magnitude * cos(z.angle); } double imaginary_part(Complex z) { return z.magnitude * sin(z.angle); } double magnitude(Complex z) { return z.magnitude; } double angle(Complex z) { return z.angle; } void print_complex(Complex z) { if (imaginary_part(z) < 0) printf("%f - i%f", real_part(z), -imaginary_part(z)); else printf("%f + i%f", real_part(z), imaginary_part(z)); } Complex add_complex(Complex c1, Complex c2) { return make_complex(real_part(c1) + real_part(c2), imaginary_part(c1) + imaginary_part(c2)); } int main(void) { Complex z1 = make_complex(1, 0); Complex z2 = make_complex(0, 1); Complex z3 = add_complex(z1, z2); printf("z1 = "); print_complex(z1); printf("\n"); printf("magnitude(z1) = %f\n", magnitude(z1)); printf("z2 = "); print_complex(z2); printf("\n"); printf("magnitude(z2) = %f\n", magnitude(z2)); printf("z3 = "); print_complex(z3); printf("\n"); printf("magnitude(z3) = %f\n", magnitude(z3)); return 0; } Utmatning --------- z1 = 1.000000 + i0.000000 magnitude(z1) = 1.000000 z2 = 0.000000 + i1.000000 magnitude(z2) = 1.000000 z3 = 1.000000 + i1.000000 magnitude(z3) = 1.414214 Notera: Helt olika implementation av Complex, men de fungerar likadant! Samma kod (main-funktionen), samma utmatning. Dvs, vi har en abstrak datatyp, och vi har abstraherat bort implementationen! Övning: Så varför skulle man vilka byta implementation? Ett annat exempel på abstraktion: Kortfärger med och utan implementationsdetaljer --------------------------------------------------------------------------------- "Specifikation" = VAD ska programmet/programdelen/datatypen göra? (Dvs: beteende, hur ska den bete sig?) "Implementation" = HUR ska den göra det? abstraktion-2.c --------------- // Ingen abstraktion: // Kortfärger lagras som heltal, och hjärter anges med talet 2 int bud; bud = 2; if (bud == 2) printf("Budet var hjärter.\n"); // Mer abstraktion: // Kortfärger lagras som heltal, och hjärter anges med namnet (makrot) HEARTS #define SPADES 1 #define HEARTS 2 #define DIAMONDS 3 #define CLUBS 4 int bud; bud = HEARTS; if (bud == HEARTS) printf("Budet var hjärter.\n"); // Ännu mer abstraktion: // Kortfärger lagras som en egen enum-typ, och hjärter anges med namnet hearts enum card_suit { spades, hearts, diamonds, clubs }; enum card_suit bud; bud = hearts; if (bud == hearts) printf("Budet var hjärter.\n"); // Ännu lite mer abstraktion: // Kortfärger lagras som en egen typ, och hjärter anges med namnet hearts enum card_suit { spades, hearts, diamonds, clubs }; typedef enum card_suit card_suit; card_suit bud; bud = hearts; if (bud == hearts) printf("Budet var hjärter.\n"); } Mer om vi hinner ---------------- Att hantera flera värden (double, heltal, poster...): fil Parametrar till main Att hantera flera värden (double, heltal, poster...): array (repetion) Pekare (repetition) malloc och free av en enda "variabel" (double, heltal, post) malloc och free av array Länkad lista: "invasiv enkellänkad lista" (double, heltal, post) Lästips om abstraktion ---------------------- "Abstraktion" i Wikipedia: http://en.wikipedia.org/wiki/Abstraction_%28computer_science%29 "Abstrakta datatyper": http://en.wikipedia.org/wiki/Abstract_data_type eller på svenska: http://sv.wikipedia.org/wiki/Datatyp#Abstrakta_typer Gunnar Jokis kompendium (på kursens hemsida): kapitel 1 (Men, en varning: ibland står det "abstrakt datatyp" i kompendiet när det egentligen borde stå "modul".) Weiss-boken ("Data Structures and Algorithm Analysis in C++, 3d Ed") sid 71 Janlert-boken ("Datatyper och algoritmer") sid 27, 40