Lezione 9: Allocazione Dinamica della Memoria

Soluzione di Esercizi

Esercizio 8.1 Scrivere una funzione, char *strw(char *s, int k, char *w), che copia la k-esima parola contenuta nella stringa s in w, come stringa, e ritorna il puntatore w. Se s contiene meno di k parole, allora la funzione ritorna NULL (NULL è una costante in stdlib.h (e in stdio.h) che rappresenta un indirizzo non valido). La funzione strw() è una versione per stringhe della funzione parola() dell'esercizio 7.1.

#include <stdio.h>
#include <ctype.h>

/* Copia in w (come stringa) la k-esima parola nella stringa s e ritorna w.
 * Se s contiene meno di k parole, ritorna NULL. */
char *strw(char *s, int k, char *w) {
    int i = 0;
    while (s[i] != '\0' && k > 0) {
        if (isalpha(s[i]) && (i == 0 || !isalpha(s[i - 1])))
            k--;
        i++;
    }
    if (k == 0) {
        int j = 0;
        i--;
        while (s[i] != '\0' && isalpha(s[i]))
            w[j++] = s[i++];
        w[j] = '\0';
        return w;
    } else
        return NULL;
}

Esercizio 8.2 Scrivere un programma (usando le funzioni anagramma() e strw()) che prende in input una linea di testo e poi una stringa e stampa tutte le parole contenute nella linea di testo che sono anagrammi della stringa.

#include <stdio.h>
#include <ctype.h>

char *strw(char *s, int k, char *w) {
    . . .
}

int anagramma(char *s1, char *s2);
int inputline(char line[], int maxlen);

#define MAXLEN    1000

//stampa le parole in una frase che sono anagrammi di un parola data
int main() {    
    char line[MAXLEN + 1];
    printf("Inserire una frase: ");
    int n = inputline(line, MAXLEN);
    line[n] = '\0';
    char p[MAXLEN + 1], w[MAXLEN + 1];
    printf("Inserire una parola: ");
    scanf("%s", p);
    int k = 1;
    while (strw(p, k, w) != NULL) {
        if (anagramma(p, w))
            printf("%s\n", w);
        k++;
    }
}

Allocazione Dinamica della Memoria

Supponiamo di voler scrivere una funzione char *dupstr(char *s) che ritorna una copia della stringa di input s. Potremmo scriverla così?

char *dupstr(char *s) {
    char dup[strlen(s) + 1];
    strcpy(dup, s);
    return dup;      //ERRORE dup è un array locale!!!
}

Questa funzione causerebbe errori perche' la memoria riservata per la variabile locale esiste solo nel contesto (scope) della funzione che la dichiara. Per scrivere una funzione di questo tipo dobbiamo allocare memoria il modo dinamico, cioe' riservare locazioni di memoria per la durata dell'esecuzione del programma. In piu', la dimensione dell'array non e' nota a priori (durante la compilazione), quindi non possiamo semplicemente allocare la memoria in modo statico nel file, ma dobbiamo allocare una dimensione di memoria determinata durante l'esecuzione del programma.

Il C supporta l'allocazione dinamica si memoria attraverso l'uso di funzione di libreria dichiara in stdlib.h Le principali funzioni sono:

Queste funzioni ritornano o accettano come argomenti puntatori di tipo

void*

che indica un puntatore ad un blocco di memoria generico. Se necessario, questo tipo e' automaticamente convertito ad altri tipi puntatore.

Per determinare la quantita' di bytes necessaria per i vari tipi si usa l'operatore

sizeof(<tipo>)
sizeof(<variabile>)

Ad esempio, sizeof(char) ritorna 1, sizeof(float) ritorna 4, mentre per una variabile v di tipo <tipo>, sizeof(v) ritorna sizeof(<tipo>). Nel caso di tipi array, l'operatore sizeof(<tipo>[<dimensione>]) e' uguale a sizeof(<tipo>)*<dimensione>.

Ora possiamo scrivere la versione corretta della funzione dupstr():

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

char *dupstr(char *s) {    //ritorna una copia della stringa s
    int n = strlen(s);
    char *dup = malloc((n + 1)*sizeof(char));
    if (dup == NULL) {
        //allocazione fallita
    }
    strcpy(dup, s);
    return dup;
}

Esempi:

Funzione int *occ(int A[], int n, int x, int *nocc) che ritorna in un array, allocato dinamicamente, le posizioni dell'array A che contengono il valore x e in *nocc restituisce il numero di tali posizioni.

#include <stdlib.h>

int *occ(int A[], int n, int x, int *nocc) {
    int *pos = NULL;
    int no = 0;
    for (int i = 0 ; i < n ; i++) {
        if (A[i] == x) {
            pos = realloc(pos, (no + 1)*sizeof(int));
            pos[no++] = i;
        }
    }
    *nocc = no;
    return pos;
}

Funzione char *inputstr() che ritorna in una stringa allocata dinamicamente la sequenza di caratteri letti dall'input (fino a EOF o '\n').

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

char *inputstr() {    
    char *str = NULL;
    int c, n = 0;
    do {
        c = getchar();
        if (c != EOF && c != '\n') {
            str = realloc(str, (n + 1)*sizeof(char));
            str[n++] = c;
        }
    } while (c != EOF && c != '\n');
    str[n] = '\0';
    return str;
}

Funzione char **wordarray(char *line, int *nw) che ritorna in un array di stringhe, allocato dinamicamente, le parole contenute nella stringa line e in *nw restituisce il numero di tali parole.

  array di stringhe
 __
|  |    -----------------
| •---->|               |
|__|    -----------------
|  |    --------------------------
| •---->|                        |
|__|    --------------------------
|  |    -----------
| •---->|         |
|  |    -----------
.  .
.  .
.  .

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

int parola(char A[], int n, int k, int *plung);

char **wordarray(char *line, int *nw) {
    char **wA = NULL;
    int len = strlen(line);
    int plen, p, n = 0;
    while ((p = parola(line, len, n + 1, &plen)) != -1) {
        wA = realloc(wA, (n + 1)*sizeof(char *));
        wA[n] = malloc((plen + 1)*sizeof(char));
        strncpy(wA[n], &(line[p]), plen);
        wA[n][plen] = '\0';
        n++;
    }
    *nw = n;
    return wA;
}

Programma che legge in input una linea di testo e ne stampa le parole contenute. Uso delle funzioni inputstr() e wordarray(); disallocazione dell'array di stringhe.

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

char **wordarray(char *line, int *nw);
char *inputstr();

int main() {
    printf("Inserire una linea di testo: ");
    char *line = inputstr();
    int nwords;
    char **warray = wordarray(line, &nwords);
    free(line);
    printf("Numero parole: %d\n", nwords);
    for (int i = 0 ; i < nwords ; i++)
        printf("%s\n", warray[i]);
    for (int i = 0 ; i < nwords ; i++) 
        free(warray[i]);
    free(warray);
}

Esercizi

Esercizio 9.1 Scrivere una funzione double *duparrayD(double A[], int n) che ritorna un duplicato (tramite array allocato dinamicamente) dell'array A.

Esercizio 9.2 Scrivere una funzione char *concat(char *s1, char *s2) che ritorna una nuova stringa che contiene la concatenazione delle stringhe s1 e s2. Ad esempio, se s1 e' "Prima parte" e s2 e' "Seconda parte" allora la funzione ritorna la stringa "Prima parteSeconda parte".