Programmering C: Lösningar till tentamen 2011-05-28

Observera att detta är förslag på lösningar. Det kan finnas andra lösningar som också är korrekta, och 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. En del av lösningarna är kanske inte fullständiga, utan hänvisar bara till var man kan läsa svaret.

Uppgift 1 (1 p)

a) 8

b) 8

c) -4

Uppgift 2 (3 p)

#include <stdio.h>

int main(void) {
    double x, y;

    printf("Ange x: ");
    scanf("%lf", &x);

    printf("Ange y: ");
    scanf("%lf", &y);

    if (y == 0)
        printf("y är noll, så x/y kan inte beräknas.\n");
    else
        printf("x/y = %f\n", x/y);

    if (x == 0)
        printf("x är noll, så y/x kan inte beräknas.\n");
    else
        printf("y/x = %f\n", y/x);

    return 0;
}

Uppgift 3 (2 p)

a = 2, b = 2, x = 2.0, y = 1.0

Uppgift 4 (2 p)

double cirkelarea(double r) {
    return M_PI * r * r;
}
Kommentar: I C-standarden C99 har man tagit bort M_PI från math.h. Då kan man själv definiera den, till exempel så här:
#define M_PI 3.14159265358979323846

Uppgift 5 (2 p)

int main(void) {
    double r, A;

    printf("Ange cirkelns radie: ");
    scanf("%lf", &r);

    A = cirkelarea(r);

    printf("Area: %f\n", A);

    return 0;
}

Uppgift 6 (3 p)

void initialer(char *helanamnet, char *initialer) {
    int namnlangd = strlen(helanamnet);
    int antal_initialer = 0;
    int i;
    for (i = 0; i < namnlangd; ++i) {
        if (i == 0 || (i >= 2 && helanamnet[i - 1] == ' ')) {
            initialer[antal_initialer++] = helanamnet[i];
        }
    }
}
Kommentar: Det är alltså den anropande funktionens ansvar att pekaren intialer pekar på en strängvariabel, och att det finns tillräckligt med plats i den för alla initialerna.

Uppgift 7 (2 p)

int main(void) {
    char namnet[200], initialerna[100];

    printf("Ange ett namn: ");
    fgets(namnet, sizeof namnet, stdin);
    initialer(namnet, initialerna);
    printf("Initialer: %s\n", initialerna);

    return 0;
}

Uppgift 8 (1 p)

#define MAX_NAMNLANGD 20

struct Apple {
    double vikt;
    char sort[MAX_NAMNLANGD + 1];
};

Uppgift 9 (1 p)

struct Apple a = { 200, "Astrakan" };

Uppgift 10 (2 p)

void visa_apple(struct Apple a) {
    printf("Vikt: %.2f gram\n", a.vikt);
    printf("Sort: %s\n", a.sort);
}

Uppgift 11 (2 p)

void las_apple(struct Apple *ap) {
    printf("Ange äpplets vikt (i gram): ");
    scanf("%lf", &ap->vikt);
    // Ta bort radslutstecknet som finns kvar i inmatningsbufferten
    while (getchar() != '\n')
        ;
    printf("Ange äppelsorten: ");
    char sort[MAX_NAMNLANGD + 1 + 1]; // En extra plats för \n
    fgets(sort, sizeof sort, stdin);
    // fgets sparar radslutstecknet i variabeln, så nu tar vi bort det
    int n = strlen(sort);
    if (sort[n - 1] == '\n')
        sort[n - 1] = '\0';
    else if (!feof(stdin)) {
        // För lång rad. Läs bort skräpet.
        while (getchar() != '\n' && !feof(stdin))
            ;
    }
    strncpy(ap->sort, sort, MAX_NAMNLANGD + 1);
    // strncpy NUL-avslutar inte målsträngen, om den är för liten
    ap->sort[MAX_NAMNLANGD] = '\0';
}
Kommentar: Man behöver inte göra det så här komplicerat för full poäng på uppgiften.

Uppgift 12 (2 p)

int main(void) {
    struct Apple a1, a2;
    las_apple(&a1);
    las_apple(&a2);
    visa_apple(a1);
    visa_apple(a2);

    return 0;
}

Uppgift 13 (5 p)

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, c, A;

    printf("Ange sidan b: ");
    scanf("%lf", &b);

    while (b > 0) {
        printf("Ange sidan c: ");
        scanf("%lf", &c);
        printf("Ange vinkeln A (i radianer): ");
        scanf("%lf", &A);

        a = sqrt(b*b + c*c - 2*b*c*cos(A));

        printf("a = %f\n", a);

        printf("Ange sidan b: ");
        scanf("%lf", &b);

    }

    return 0;
}

Uppgift 14 (6 p)

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

int main(void) {
    char filnamn[100 + 1];

    printf("Ange filnamnet: ");
    fgets(filnamn, sizeof filnamn, stdin);
    // fgets sparar radslutstecknet i variabeln, så nu tar vi bort det
    int n = strlen(filnamn);
    if (filnamn[n - 1] == '\n')
        filnamn[n - 1] = '\0';

    FILE *fil;
    fil = fopen(filnamn, "r");
    if (fil == NULL) {
        fprintf(stderr, "Det gick inte att öppna filen '%s'.\n", filnamn);
        exit(EXIT_FAILURE);
    }

    double tal;
    unsigned long antal = 0;
    double summa = 0;

    while (fscanf(fil, "%lf", &tal) == 1) {
        ++antal;
        summa += tal;
    }
        
    double medel = summa / antal;

    rewind(fil); // Man kan öppna den på nytt också

    unsigned long antal_avvikande = 0;
    
    while (fscanf(fil, "%lf", &tal) == 1) {
        if (tal < medel * 0.9 || tal > medel * 1.1)
            ++antal_avvikande;
    }

    fclose(fil);

    printf("Totalt antal tal: %lu\n", antal);
    printf("Antal avvikande: %lu\n", antal_avvikande);

    return 0;
}

Uppgift 15 (6 p)

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

#define MAX_ANTAL_UNIKA_TAL 1000000

double unika_tal[MAX_ANTAL_UNIKA_TAL];
int antal_unika = 0;

int finns_redan(double tal) {
    for (int i = 0; i < antal_unika; ++i)
        if (unika_tal[i] == tal)
            return 1;
    return 0;
}

int main(void) {
    char infilnamn[100 + 1];

    printf("Ange infilnamnet: ");
    fgets(infilnamn, sizeof infilnamn, stdin);
    // fgets sparar radslutstecknet i variabeln, så nu tar vi bort det
    int n = strlen(infilnamn);
    if (infilnamn[n - 1] == '\n')
        infilnamn[n - 1] = '\0';

    char utfilnamn[100 + 1];

    printf("Ange utfilnamnet: ");
    fgets(utfilnamn, sizeof utfilnamn, stdin);
    // fgets sparar radslutstecknet i variabeln, så nu tar vi bort det
    n = strlen(utfilnamn);
    if (utfilnamn[n - 1] == '\n')
        utfilnamn[n - 1] = '\0';

    FILE *infil;
    infil = fopen(infilnamn, "r");
    if (infil == NULL) {
        fprintf(stderr, "Det gick inte att öppna indatafilen '%s'.\n", infilnamn);
        exit(EXIT_FAILURE);
    }

    FILE *utfil;
    utfil = fopen(utfilnamn, "w");
    if (utfil == NULL) {
        fprintf(stderr, "Det gick utte att öppna utdatafilen '%s'.\n", utfilnamn);
        exit(EXIT_FAILURE);
    }

    double tal;

    while (fscanf(infil, "%lf", &tal) == 1) {
        if (finns_redan(tal) == 0) {
            // Skriv det nya unika talet på filen...
            fprintf(utfil, "%f\n", tal);
            // ...och spara det i arrayen med unika tal
            unika_tal[antal_unika++] = tal;
        }
        // Om det var en dubblett struntar vi bara i det
    }
        
    fclose(infil);
    fclose(utfil);

    return 0;
}


Thomas Padron-McCarthy (thomas.padron-mccarthy@oru.se), 28 april 2011