Sistemi di Calcolo

Corso di Laurea in Ingegneria Informatica e Automatica - A.A. 2017-2018

HomePage | Avvisi | Diario lezioni | Programma | Materiale didattico | Esami | Forum | Login

Esercitazione 1 - 7 ottobre 2016 (150 min)


Configurazione

Nelle esercitazioni lavoreremo con Linux LXLE nella VM Oracle Virtualbox BIAR.

Per questa prima esercitazione, può essere utile avere a disposizione le pagine man di alcune funzioni C. Seguire i seguenti passi:

Creare una directory di lavoro sc1617 e posizionarsi nella directory creata come segue:
$ mkdir /home/biar/Desktop/sc1617
$ cd /home/biar/Desktop/sc1617


Esercizio 1 (duplicazione di stringhe)

L'obiettivo di questo esercizio è individuare e correggere un errore comune nell'utilizzo di funzioni di libreria C. Si immetta il seguente programma di esempio nel file strdup.c:

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

int main (int argc, char* argv[]) {

    char* prog_name = strdup(argv[0]);
    printf("binary name: %s\n", prog_name);
 
    return 0;
}

Compilare il programma includendo le informazioni di debugging, quindi farlo eseguire all'interno valgrind.
$ gcc -g -o strdup strdup.c
$ valgrind ./strdup

Che tipologia di errore viene riportato? Correggerlo, quindi rieseguire nuovamente il programma usando valgrind.

Esercizio 2 (concatenazione di stringhe)

Il seguente frammento di codice può portare ad una corruzione dell'immagine di memoria di un processo:

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

#define BUFLEN 16

int main() {
    char s1[BUFLEN], s2[BUFLEN];

    strcpy(s1,  "This is source");
    sprintf(s2, "%s", "This is destination");

    strcat(s2, s1);

    printf("Final string: |%s|", s2);
    return 0;
}

Per scoprire cosa fanno le funzioni strcpy, sprintf e strcat consultare la sezione 3 del manuale in linea (ad esempio, man 3 strcat). Compilare ed eseguire il programma come per l'Esercizio 1: che tipo di errore si verifica? Proporre una soluzione al problema.

Esercizio 3 (manipolazione di stringhe)

Il seguente programma manipola una stringa in input mettendo ogni lettera al suo interno in lowercase:

tolower.c
#include <stdio.h>
#include <string.h>
#include <ctype.h>

char* to_lower (const char *str) {
    char* l = strdup(str);
    char* c;

    for (c = l; *c; c++) {
        if (isupper(*c))
            *c = tolower(*c);
    }

    return l;
}

int main (int argc, char* argv[])
{
    if (argc != 2) return 1;

    char* lower = to_lower (argv[1]);
    while (*lower)
        printf("%c", (*(lower++)));
    printf("\n");

    return 0;
}

Quali errori sono presenti al suo interno?

Esercizio 4 (navigazione nell'Atlantico)

Il codice seguente è stato adattato da un programma che simula l'avanzamento dell'RMS Titanic nell'Oceano Atlantico:

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

#define BOAT_SPEED 1

char* full_steam_ahead(unsigned current_distance) {
    unsigned new_distance = current_distance + BOAT_SPEED;

    char* log_text_suffix = "miles, go ahead!\n";
    int buffer_length = strlen("log_text_suffix") + 3;

    char* buffer;
    sprintf(buffer, "%u %s", new_distance, log_text_suffix);
    return buffer;
}

int main(int argc, char* argv[]) {
    int to_the_atlantic;
    int nautical_miles;

    // check number of arguments
    if (argc < 2) {
        printf("Syntax: %s <miles>\n", argv[0]);
        return 1;
    }

    // parse argv[1]'s content as an int using atoi()
    to_the_atlantic = atoi(argv[1]);
    if (to_the_atlantic < 1) {
        printf("Specify a positive (non-zero) amount!\n");
        return 1;
    }

    // advance the boat towards the Atlantic as requested by the user
    char* captains_log;
    for (nautical_miles = 1; nautical_miles <= to_the_atlantic; nautical_miles++) {
        captains_log = full_steam_ahead(nautical_miles);
        printf("%s", captains_log);
    }
    free(captains_log);

    return 0;
}

Al suo interno sono presenti un certo numero di bug identificabili tramite esecuzione sotto valgrind. Individuarli utilizzando più valori di input (#argv[1]# conterrà il numero di miglia marine da percorrere) e correggerli.

Esercizio 5 (liste collegate)

Di seguito è riportata una implementazione buggata di una lista collegata in C:

list.c
#include <stdio.h>
#include <stdlib.h>

typedef struct node {
    char * id;
    char * value;
    struct node * next;
} node_t;

static int count = 0;

node_t* add_node(node_t* l, char* value) {
    node_t* node = malloc(sizeof(node_t));

    char id[16];
    sprintf(id, "ID_%d", count++);
    node->id = id;
    node->value = value;

    if (l != NULL)
        node->next = l;
   
    return node;
}

void print_list(node_t* head) {
    node_t* current = head;

    while (current != NULL) {
        printf("%s\n", current->value);
        current = current->next;
    }
}

void delete_list(node_t * head) {
    node_t* current = head;

    while (current != NULL) {
        free(current);
        free(current->value);
        current = current->next;
    }
}

int main() {
    node_t* l;

    l = add_node(NULL, "Hello");
    l = add_node(l, " ");
    l = add_node(l, "World");
    l = add_node(NULL, "!");

    print_list(l);
    delete_list(l);
}

Individuare gli errori con l'ausilio di valgrind e proporre una soluzione che utilizzi correttamente la memoria.

Esercizio 6 (debugging in gdb)

Il frammento di codice seguente calcola l'i-esimo numero della successione di Fibonacci specificato dall'utente. In particolare, esegue una implementazione ricorsiva ed una variante iterativa dell'algoritmo di calcolo:

fibonacci.c
#include <stdio.h>
#include <stdlib.h>

// https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Fibonacci_Number_Program#Recursive_version
unsigned int fib(unsigned int n) {
    if (n < 2) return n;
    else return fib(n - 1) + fib(n - 2);
}

// https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Fibonacci_Number_Program#Iterative_version
unsigned int fib_iter(unsigned int n) {
    unsigned int i = 0, j = 1, k, t;
    for (k = 1; k <= n; ++k) {
        t = i + j;
        i = j;
        j = t;
    }
    return j;
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
    printf("Syntax: %s <n>\n", argv[0]);
        return 1;
    }

    unsigned int n = atoi(argv[1]);
    printf("[%u] ric: %u iter: %u\n", n, fib(n), fib_iter(n));

    return 0;
}

I due metodi fib e fib_iter sono stati presi da Wikibooks ma restituiscono risultati difformi. Risalire alla causa di questa difformità con l'ausilio di gdb, in particolare utilizzando breakpoint (possibilmente condizionali) per ispezionare il compo