Programmering C: Lösningar till tentamen 2018-06-02

Observera att detta är förslag på lösningar. Det kan finnas andra lösningar som också är korrekta. Det kan hända att en del av lösningarna är mer omfattande än vad som krävs för full poäng på uppgiften, eller att de bara hänvisar till var man kan läsa svaren. Dessutom har det inträffat i världshistorien att lärare skrivit fel i lösningsförslagen. Jag har förstås aldrig gjort det, men andra. Är det verkligen någon som läser såna här inledande texter? Jag vet inte. Det kan vara så. Rabarber rabarber rabarber. Det har jag hört att statisterna får säga på filminspelningar när det ska vara bakgrundssorl från en restaurang eller liknande. Här nedan kommer lösningsförslagen till uppgifterna.

Uppgift 1 (1 p)

a) 9
b) 7
c) 7
d) 4

Uppgift 2 (3 p)

a = 2, b = 3
x = 3
x = 3
x = 3
a = 2, b = 3

Uppgift 3 (5 p)

a) 9
b) 11
c) 10
d) 10
d) 11

Uppgift 4 (6 p)

#include <stdio.h>
#include <float.h>

double max2(double a[], int antal_tal) {
    double max = a[0];
    for (int i = 0; i < antal_tal; ++i) {
        if (a[i] > max)
            max = a[i];
    }
    double max2 = DBL_MIN; // Vi börjar inte med a[0], om a[0] är max!
    for (int i = 0; i < antal_tal; ++i) {
        if (a[i] > max2 && a[i] < max)
            max2 = a[i];
    }
    return max2;
}

#define ANTAL_TAL 57

int main(void) {
    double a[ANTAL_TAL];
    printf("Skriv %d reella tal:\n", ANTAL_TAL);
    for (int i = 0; i < ANTAL_TAL; ++i)
        scanf("%lf", &a[i]);
    double nast_hogsta = max2(a, ANTAL_TAL);
    printf("Näst högsta talet = %f\n", nast_hogsta);
    return 0;
}

Uppgift 5 (5 p)

#include <stdio.h>

int main(void) {
    double fakturerat_belopp;
    printf("Ange fakturerat belopp: ");
    scanf("%lf", &fakturerat_belopp);
    double moms = fakturerat_belopp * 0.25;
    double totalbelopp = fakturerat_belopp + moms;
    double oren = totalbelopp - (int)totalbelopp;
    double oresutjamning;
    if (oren >= 0.50)
        oresutjamning = 1 - oren;
    else
        oresutjamning = -oren;
    double belopp_att_betala = totalbelopp + oresutjamning;

    printf("\n");
    printf("Fakturerat belopp: %10.2f kr\n", fakturerat_belopp);
    printf("Moms 25%%:          %10.2f kr\n", moms);
    printf("Totalbelopp:       %10.2f kr\n", totalbelopp);
    printf("Öresutjämning:     %10.2f kr\n", oresutjamning);
    printf("Belopp att betala: %7.0f kr\n", belopp_att_betala);
    return 0;
}

Uppgift 6 (5 p)

Om man är bekant med tillståndsmaskiner, kan man tänka sig detta som en tillståndsmaskin med två tillstånd: antingen befinner vi oss inuti ett ord, eller inte. Vi börjar i tillståndet att vi inte är inuti ett ord. Vi tittar på ett tecken i taget, Beroende på vad detta tecken är, ska vi antingen skriva ut tecknet eller inte, och antingen byta till det andra tillståndet eller inte.
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main(void) {
    char rad[100 + 1 + 1]; // +1 för '\n' och +1 för '\0'
    fgets(rad, sizeof(rad), stdin);
    int inuti_ett_ord = 0;
    int n = strlen(rad);
    for (int i = 0; i < n; ++i) {
        unsigned char detta_tecken = rad[i]; // Unsigned för ctype-makrona
        if (inuti_ett_ord) {
            if (isalpha(detta_tecken)) {
                // Vi var redan inuti ett ord, och ordet fortsätter.
            }
            else if (isspace(detta_tecken)) {
                // Vi var inuti ett ord, men nu kom ett blanktecken.
                inuti_ett_ord = 0;
            }
            else {
                // Vi var inuti ett ord, men nu kom ett skiljetecken.
                inuti_ett_ord = 0;
                putchar(detta_tecken);
            }
        }
        else {
            if (isalpha(detta_tecken)) {
                // Vi var inte inuti ett ord, men nu började ett ord.
                inuti_ett_ord = 1;
                putchar(detta_tecken);
            }
            else if (isspace(detta_tecken)) {
                // Vi var inte i ett ord, och nu kom ett blanktecken.
            }
            else {
                // Vi var inte i ett ord, och nu kom ett skiljetecken.
                putchar(detta_tecken);
            }
        }
    }
    printf("\n");
    return 0;
}

Uppgift 7 (7 p)

På riktigt skulle man förstås ha en del felhantering i programmet, till exempel för att hantera att man försöker boka en plats som redan är upptagen.
#include <stdio.h>
#include <string.h>
#include <ctype.h>

void visa_salongen(char bokade_platser[]) {
    printf("Lediga platser:\n");
    printf("\n");
    for (int plats = 0; plats < 120; ++plats) {
        if (bokade_platser[plats] == 0)
            printf("%5d", plats + 1);
        else
            printf("     ");
        if ((plats + 1) % 10 == 0)
            printf("\n");
    }
    printf("\n");
}

int las_plats(char bokade_platser[]) {
    visa_salongen(bokade_platser);
    printf("Ange platsnummer (avsluta med 0): ");
    int onskad_plats;
    scanf("%d", &onskad_plats);
    return onskad_plats;
}

int main(void) {
    char bokade_platser[120];
    // Alla obokade från början
    for (int plats = 0; plats < 120; ++plats)
        bokade_platser[plats] = 0;

    int onskad_plats = las_plats(bokade_platser);
    while (onskad_plats != 0) {
        bokade_platser[onskad_plats - 1] = 1;
        onskad_plats = las_plats(bokade_platser);
    }

    return 0;
}

Uppgift 8 (4 p)

För enkelhets skull antar vi att en int är tillräckligt stor för att innehålla en miljard, även om C-standarden bara garanterar att de kan innehålla tal upp till och med 32767. Vanliga datorer idag har 32-bitarsheltal, och kan lagra tal upp till och med 2147483647.

Filen vi skapar här är en textfil. Den blir ungefär tio gigabyte stor, och på min dator tog det här programmet ungefär en minut att köra. Programmet i nästa uppgift tog ungefär lika lång tid.

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void) {
    srand(time(NULL));
    FILE *f = fopen("1Gtal.txt", "w");
    for (int i = 0; i < 1000000000; ++i)
        fprintf(f, "%d\n", rand());
    fclose(f);
    return 0;
}

Uppgift 9 (4 p)

Observera att man inte behöver läsa in alla de en miljard talen och lagra dem i primärminnet. De skulle visserligen få plats (fyra gigabyte) på en vanlig modern dator (som oftast har minst åtta gigabyte minne), men det är onödigt, och skulle göra programmet längre, mer komplicerat och långsammare.
#include <stdio.h>

int main(void) {
    FILE *f = fopen("1Gtal.txt", "r");
    int detta_tal;
    int antal_tal; // På dagens vanliga datorer är int 32 bitar, så en miljard får plats
    double summa = 0; // En int skulle ge overflow!
    while (fscanf(f, "%d", &detta_tal) != EOF) {
        ++antal_tal;
        summa += detta_tal;
    }
    fclose(f);
    printf("Medlevärdet: %f\n", summa / antal_tal);
    return 0;
}


Thomas Padron-McCarthy (thomas.padron-mccarthy@oru.se), 10 juni 2018