Lezione 18
Discussione esercizio 33:
#include <stdlib.h>
#include <string.h>

/* Ordina la lista di parole L rispetto al campo word e ritorna il puntatore 
 * alla lista ordinata. Usa una versione ricorsiva del selection-sort. */
WList wl_sort(WList L) {
    if (L == NULL || L->next == NULL) return L;
    WElem **min = &L;  //locazione di memoria che contiene il puntatore
                       //all'elemento minimo finora trovato.
    WElem *p = L;
    while (p->next != NULL) {
        if (strcmp(p->next->word, (*min)->word) < 0)
            min = &(p->next);
        p = p->next;
    }
    WElem *first = *min;    //puntatore all'elemento minimo
    *min = first->next;     //sgancia dalla lista l'elemento minimo
    first->next = wl_sort(L);
    return first;
}

Discussione esercizio 34:
/* Ritorna vero se le due liste di parole L1 e L2 sono uguali. Per uguali si 
 * intende che devono avere la stessa lunghezza e gli elementi in posizioni 
 * corrispondenti devono avere lo stesso contenuto. */
int wl_equal(WList L1, WList L2) {
    while (L1 != NULL && L2 != NULL) {
        if (strcmp(L1->word, L2->word) != 0)
            return 0;
        if (L1->count != L2->count)
            return 0;
        L1 = L1->next;
        L2 = L2->next;
    }
    return (L1 == L2);
}

Il  main con argomenti : la funzione main ha un'altra versione che permette di prendere degli argomenti direttamente dalla linea di comando:
int main(int argc, char *argv[]) {
     ...
}
    argc     numero argomenti
    argv[i]  i-esimo argomento, però argv[0] è il nome del programma
             quindi gli argomenti iniziano dall'indice 1
Esempio: programma che prende come argomenti una sequenza di numeri interi e ne stampa la somma (la funzione  sscanf()  in stdio.h).
#include <stdio.h>

int main(int argc, char *argv[]) {    //stampa la somma degli argomenti 
    int s = 0;
    for (int i = 1 ; i < argc ; i++) {
        int k;
        sscanf(argv[i], "%d", &k);
        s += k;
    }
    printf("La somma e' %d\n", s);
}
Se il programma compilato si chiama sum, può essere eseguito da terminale nel seguente modo:
> sum 1 2 3 4 5
> La somma e' 15

 Puntatori a funzioni . Anche le funzioni hanno un tipo. Il tipo di una funzione è determinato dal tipo ritornato e dalla sequenza dei tipi degli argomenti.
int f();           //Tipo: funzione che ritorna int
int f2(double);    //Tipo: funzione con argomento double e ritorna int
void f3(int, int); //Tipo: funzione con argomenti int, int e ritorna void
A differenza degli altri tipi non è possibile definire variabili di tipo funzione, però si possono definire variabili di tipo puntatore a funzione:
tipo-ritornato (*pf)(argomenti);   //pf è una variabile di tipo puntatore a funzione
La sintassi per dichiarare un puntatore a funzione è del tutto simile a quella per dichiarare il prototipo di una funzione, con la differenza che al posto del nome della funzione c'è (*nome_puntatore_a_funzione). Ecco alcuni esempi:
int funz() { ... }
void funz2(double a[], int n) { ... }

int main() {
    double A[10];
    int (*pf)(void);              //pf è una variabile di tipo puntatore a funzione senza argomenti che ritorna int
    void (*pf2)(double [], int);  //pf2 è una variabile di tipo puntatore a funzione con argomenti
                                  //array di double, int e che ritorna void
    pf = &funz;     //&funz è il puntatore alla funzione funz
    (*pf)();        //equivale alla chiamata funz();
    pf();           //equivale a (*pf)();
    pf2 = funz2;    //equivale a pf2 = &funz2;
    pf2(A, 10);     //equivale alla chiamata funz2(A, 10);
}
Per i tipi funzione vale una regola simile a quella che vale per i tipi array: una espressione di tipo funzione è automaticamente convertita al corrispondente tipo puntatore a funzione eccetto quando è usata in una chiamata (di funzione), come argomento dell'operatore indirizzo (&) o come argomento dell'operatore sizeof (è invalido applicare l'operatore sizeof a una espressione di tipo funzione). Così, l'assegnamento pf2 = funz2 non necessita dell'operatore indirizzo perché funz2 è automaticamente convertito al corrispondente tipo puntatore.
Vale anche un'altra regola, un'espressione di tipo puntatore a funzione può essere usata in una chiamata di funzione senza un deferenziamento esplicito. Questa è la ragione per cui si può scrivere, nell'esempio sopra, pf() in luogo di (*pf)().

Le seguenti dichirazioni di variabili non sono equivalenti:
int (*pfA)();
int (*pfB)(void);
La prima, pfA, è una specie di puntatore generico a una funzione che ritorna int, senza vincoli sul numero e tipo degli argomenti. Mentre la seconda, pfB, specifica un puntatore a funzione senza argomenti che ritorna int. Così, si ha che
int funz1() { ... }
int funz2(double a[], int n) { ... }

int main() {
    int (*pfA)(), (*pfB)(void);
    pfA = funz1;
    pfB = funz1;
    pfA = funz2;     //OK, il compilatore non segnala alcun errore
    pfA();           //OK, ma si possono verificare errori in esecuzione
    pfA("abc");      //OK, ma si possono verificare errori in esecuzione
    pfB = funz2;     //ERRORE o WARNING: il compilatore segnala l'incompatibilità dei tipi
}
Grazie ai puntatori a funzione si possono definire funzioni che prendono come argomenti altre funzioni. Così, ad esempio, si può definire una funzione che può ordinare un array di qualsiasi tipo (array di int, array di double, array di stringhe, ecc.). Infatti, per ordinare un array di T è sufficiente sapere come confrontare due elementi di tipo T e questo può essere specificato da un'opportuna funzione. Ciò è proprio quello che fa la funzione qsort della libreria standard (in stdlib.h):
void qsort(void *base, size_t count, size_t size, int (cmp*)(const void *e1, const void *e2));
dove il tipo size_t è un sinonimo di un tipo intero (generalmente unsigned long). Gli argomenti hanno il seguente significato: base è l'indirizzo del primo elemento dell'array da ordinare; count è il numero di elementi; size è la dimensione in bytes di un elemento; infine cmp è il puntatore a una funzione che confronta gli elementi e che ritorna
    -1  se  e1 < e2
     0  se  e1 = e2
     1  se  e1 > e2
Se si vogliono ordinare array di interi è sufficiente definire una funzione per confrontare interi:
int cmpInt(const void *e1, const void *e2) {
    int v1 = *((int *)e1), v2 = *((int *)e2);
    return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
}
Dopo di che per ordinare un qualsiasi array A di n interi basta scrivere
    qsort(A, n, sizeof(int), cmpInt);
Per ordinare array di stringhe basta definire una funzione per confrontare stringhe:
int cmpStr(const void *e1, const void *e2) {
    return strcmp(*(char **)e1, *(char **)e2);
}
Un qualsiasi array S di n stringhe può essere ordinato così:
    qsort(S, n, sizeof(char *), cmpStr);
Si tenga presente che qsort usa un algoritmo molto più efficiente degli algoritmi di ordinamento semplici come bubble-sort o selection-sort. Ad esempio, ordina un array di 100000 interi almeno 1500 volte più velocemente.

Un altro esempio è una funzione che applica una funzione data a tutti gli elementi di una lista di interi.
typedef struct Elem {
    int          val;
    struct Elem *next;
} Elem, *List;

void apply(List L, void (*f)(Elem *e)) {
    Elem *e = L;
    while (e != NULL) {
        f(e);
        e = e->next;
    }
}
Può essere usata come segue:
void square(Elem *e) {
    e->val *= e->val;
}

void printE(Elem *e) {
    printf("%d\n", e->val);
}

int main() {
    ...
    apply(L, printE);    //Stampa tutti i valori della lista L
    apply(L, square);    //Eleva al quadrato tutti i valori della lista L
    ...
}
Un'altro esempio consiste nella possibilità di definire una funzione che gestisce un menu testuale stampando le voci del menu ed eseguendo l'operazione scelta dall'utente:
#include <stdio.h>
#include "util.h"

typedef void (*OpFunc)(void);      //Puntatore a una funzione che esegue una operazione del menu

/* Prende in input un array mItem di n voci del menu e un array mOp che per ogni 
 * voce contiene la corrispondente operazione (puntatore a funzione) da eseguire. */
void menu(char *mItem[], OpFunc mOp[], int n) {
    int i, choice;
    while (1) {
        for (i = 1 ; i <= n ; i++)                 //Stampa il menu
            printf("%2d) %s\n", i, mItem[i - 1]);
        choice = input_int();                      //Legge la scelta dell'utente
        if (choice >= 1 && choice <= n) {          //Se la scelta è valida,
            mOp[choice - 1]();                     //esegue l'operazione corrispondente
            if (choice == n) return;
        } else
            printf("Scelta non valida\n");
    }
}

/* Esempio di utilizzo */
void oper1() { printf("Operazione 1\n"); }
void oper2() { printf("Operazione 2\n"); }
void fine() { printf("Fine\n"); }

int main() {
    char *m[3] = {"Operazione 1", "Operazione 2", "Fine"};
    OpFunc op[3] = {oper1, oper2, fine};
    menu(m, op, 3);
    return 0;
}
Esercizio 35    Considerando il tipo:
typedef struct {
    char cognome[40];    //stringa
    char nome[20];
} Persona;
scrivere una funzione void sort_pers(Persona P[], int n) che ordina l'array P (di n elementi) rispetto al campo cognome tramite la funzione qsort().
Esercizio 36    Scrivere una funzione List filter(List L, int (*f)(int v)) che elimina dalla lista di interi L gli elementi p tali che f(p->val) ≠ 0 e ritorna il puntatore alla lista modificata.