APPUNTI SUL LINGUAGGIO C Strutture Dati e Puntatori Corso di Algoritmi e Strutture Dati APPUNTI SUL LINGUAGGIO C Strutture Dati e Puntatori
Tipi di dato int, double, char, “boolean”, … Tipi di dati complessi : Tipi di dato elementare: int, double, char, “boolean”, … Tipi di dati complessi : Array monodimensionali Array multidimensionali Puntatori Struct (record)
typedef < tipo > < nuovo_nome_tipo > ; Il C permette di definire esplicitamente nomi nuovi per i tipi di dati, tramite la parola chiave typedef. L'uso di typedef consente di rendere il codice più leggibile. Il formato dell'istruzione typedef è il seguente: typedef < tipo > < nuovo_nome_tipo > ; in questo modo assegnamo al tipo tipo il nuovo nome nuovo_nome_tipo. Da questo momento in avanti potremo riferirci al tipo di dato tipo sia con il nome tipo sia con il nome nuovo_nome_tipo. Es: typedef int intero; intero i; /* definisce una variabile i di tipo int. */
Stringhe Una stringa è un vettore di caratteri che contiene il carattere terminatore '\0' che indica che i successivi caratteri non sono significativi. L E S T R I N G H \0 I caratteri compresi fra il primo e il carattere '\0' sono quelli significativi della stringa. Dal momento che questo è il modo per indicare dove la stringa finisce, questo carattere deve apparire almeno una volta nel vettore.
Stringhe Es. #define Dim 5 … char ch[Dim]; char s[]=“prova”; p r o v a \0 v r o p s[0] s[1] s[2] s[3] s[4] s[5]
Formattazione della stringa da parte del compilatore char s[]=“Esempio"; char str[100]; scanf(“%s”,str); // inserisco da input esempio E S E M P I O \0
Dichiarazione e inizializzazione dei vettori I vettori occupano dello spazio in memoria. Il programmatore specificherà il tipo di ogni elemento e il numero di quelli richiesti da ognuno dei vettori, così che il computer possa riservare l’appropriata quantità di memoria. #define M 3 #define N 2 ... int c[M]; int l[N]; int vet[5] = {1, 2, 3, 4, 5}; float r[] = {1.4, 3.2, 5.4 } /* viene allocato un vettore di tre elementi */ È possibile inizializzare un array in fase di dichiarazione. In questo caso, per un vettore, la specifica delle dimensioni è opzionale.
Dichiarazione e inizializzazione dei vettori I vettori occupano dello spazio in memoria. Il programmatore specificherà il tipo di ogni elemento e il numero di quelli richiesti da ognuno dei vettori, così che il computer possa riservare l’appropriata quantità di memoria. #define M 3 #define N 2 ... int c[M]; int l[N]; char vet[] = “blu”; char r[] = {‘b’, ‘l’, ‘u’, ‘\0’ } È possibile inizializzare una stringa in fase di dichiarazione a carico dell’utente. In questo caso, bisogna indicare anche \0
Formattazione della stringa da parte dell’utente char str[100]; char a; int i=0; // inserisco da input esempio for (i=0; i<7; i++) { scanf(“%c”,&a); str[i]=a; } str[i] = “/0”; E S E M P I O \0
Stringhe Il C non ha un sistema semplice per costruire le stringhe, così le seguenti assegnazioni non sono valide: char nome[50], cognome[50], nomecognome[50]; nome = "Mario" /* illegale */ cognome = "Rossi" /* illegale */ nomecognome = "Sig."+firstname+lastname /* illegale */ Comunque esiste una libreria di routines per il trattamento delle stringhe ("<string.h >").
Libreria <string.h> #include <string.h> int strcmp(char *string1, char *string2) - Confronta string1 e string. void strcpy(char *string1, char *string2) - Copia string2 in stringl. char *strerror(int errnum) - Ottiene il messaggio di errore corrispondente al numero di errore specificato. int strlen(char *string) - Determina la lunghezza di una stringa. char *strncat(char *string1, char *string2, size_t n) - Aggiunge "n" caratteri di string2 in string1. int strncmp(char *string1, char *string2, size_t n) - Confronta i primi "n" caratteri di due stringhe. char *strncpy(char *string1, char *string2, size_t n) - Copia i primi "n" char *strnset(char *string, char c, size_t n) - Setta i primi "n" caratteri di string a "c". char *strrchr(char *string, char c) - Cerca l'ultima occorrenza del carattere "c" in string.
Struct (record) Le strutture sono collezioni di variabili correlate sotto un unico nome. Le strutture possono contenere variabili di diversi tipi di dato (contrariamente ai vettori, che contengono soltanto elementi dello stesso tipo di dato) Le strutture sono un tipo di dato derivato (sono cioè costruite usando oggetti di altri tipi).
Tipi di dato Struct (record) Puntatori Liste concatenate
In C i record si definiscono mediante il costrutto struct. Struct (record) In C i record si definiscono mediante il costrutto struct. La parola chiave struct introduce la definizione della struttura, e l’identificatore persona è la structure tag (l’etichetta della struttura) /* definizione del record persona */ struct persona { stringa nome; stringa cognome; stringa CF; int eta; } ... /* dichiarazione di variabili di tipo persona */ struct persona pino,gino,nina; Le variabili dichiarate all’interno delle parentesi graffe della definizione sono i campi della struttura.
In C i record si definiscono mediante il costrutto struct. Struct (record) In C i record si definiscono mediante il costrutto struct. /* definizione del record persona */ struct persona { stringa nome; stringa cognome; stringa CF; int eta; } ... /* dichiarazione di variabili di tipo persona */ struct persona pino,gino,nina; Per dichiarare le variabili di quel tipo struttura bisogna utilizzare l’etichetta della struttura con anteposta la parola chiave struct
Per accedere a un campo del record si usa l’operatore punto. Struct (record) Per accedere a un campo del record si usa l’operatore punto. struct <nome_record> { <tipo> nome1; <tipo> nome2; … <tipo> nome3; } struct <nome_record> st; … st.nome2;
struct <nome_record> { <tipo> nome1; <tipo> nome2; … Struct (record) Se abbiamo un puntatore al record possiamo accedere ai sui campi attraverso l’operatore ->. struct <nome_record> { <tipo> nome1; <tipo> nome2; … <tipo> nome3; } struct <nome_record> *st; … st->nome2;
Struct (record) ... struct persona tizio; struct persona *p; struct persona amici[10]; scanf(“%s”, tizio.nome); tizio.eta++; printf(“%s”, amici[3].cognome); p = &tizio; printf(“%s”,p->cognome);
Definizioni alternative di record Ci sono vari modi per definire record oltre quello visto. /* dichiarazione di variabili record contestuale alla definizione del record corrispondente */ struct contocorrente { int numero; char cognome[20]; char indirizzo[30]; long int saldo; } uno,due,tre;
Definizioni alternative di record Ci sono vari modi per definire record oltre quello visto. /* dichiarazione di variabili record senza definizione del record corrispondente */ struct { int codice; char descrizione[20]; float dato; } a,b,c;
Definizioni alternative di record Ci sono vari modi per definire record oltre quello visto. /* definizione di tipi di record mediante il costrutto typedef */ typedef struct { char nome[20]; char cognome[20]; char CF[16]; int eta; } persona; … persona maria, marco;
È permessa la nidificazione di record. Proprietà dei record È permessa la nidificazione di record. struct persona { char nome[20]; char cognome[20]; struct { int giorno; int mese; int anno; } datanascita; char indirizzo[20]; } squadra[20]; ... printf(“%d”,squadra[2].datanascita.anno);
È permesso l’autoriferimento mediante i puntatori. Proprietà dei record È permesso l’autoriferimento mediante i puntatori. struct persona { char nome[20]; char cognome[20]; struct persona *coniuge; } io; struct elemento { int info; struct elemento *next; } lista;
Inizializzare i record struct card { char face[20]; char suit[20]; } … struct card a = {“Three”, “Hearts”};
Allocazione dinamica della memoria L'allocazione dinamica della memoria consente una efficace gestione della memoria a tempo di esecuzione. Si usa generalmente per la gestione di dati di cui non è nota a priori la dimensione. In ANSI C ci sono quattro funzioni per gestire dinamicamente la memoria: • malloc • calloc • realloc • free Queste funzioni sono contenute nella libreria: stdlib.h
Allocazione dinamica della memoria Operativamente la gestione avviene mediante l’uso di puntatori e della costante simbolica predefinita NULL (che vale 0 ed è definita nella libreria stdio.h). La costante NULL serve a gestire la situazione in cui un puntatore non punta a nessuna locazione di memoria.
void *malloc( size_t size ) Alloca in maniera dinamica una zona di memoria della dimensione specificata in numero di byte. Restituisce un puntatore di tipo void all'area di memoria allocata. Se non c'è sufficiente quantità di memoria, restituisce NULL. Viene sempre seguita da una operazione di "casting" per restituire un puntatore del tipo desiderato. void *malloc( size_t size )
size_t sizeof(<variabile o tipo>) Malloc Si usa in genere insieme alla funzione C sizeof che restituisce la dimensione in numero di byte di una variabile o di un tipo. size_t sizeof(<variabile o tipo>)
Malloc #include <stdio.h> #include <stdlib.h> ... typedef struct { char nome[20]; char cognome[20]; int telefono; } persona; .... /* definizione puntatori */ int *p, *q; persona *tizio;
/* allocazione dinamica di memoria */ p=(int*)malloc(sizeof(int)); q=(int*)malloc(sizeof(*q)); tizio=(persona*)malloc(sizeof(persona)); if (tizio==NULL) printf("Out of memory"); Ho allocato una cella di memoria per contenere un intero Ho allocato una cella di memoria per contenere un intero Ho allocato una cella di memoria per contenere una struttura di tipo persona
Malloc /* allocazione dinamica di memoria */ p=(int*)malloc(sizeof(int)); q=(int*)malloc(sizeof(*q)); tizio=(persona*)malloc(sizeof(persona)); if (tizio==NULL) printf("Out of memory"); ... /* uso delle variabili allocate */ scanf("%d",p); scanf("%d",q); printf("%d\n",*p + *q); tizio->telefono=1733; printf("%d",tizio->telefono);
Calloc e Realloc La calloc alloca in maniera dinamica una zona di memoria per memorizzare n oggetti della dimensione specificata. La realloc rialloca uno spazio di memoria precedentemente allocato con una calloc o malloc (si usa in genere per modificare dimensione e/o tipo di un'area di memoria già allocata).
Calloc e Realloc void *calloc( size_t n_elem, size_t elem_size ) In entrambi i casi viene restituito un puntatore di tipo void all'area di memoria (ri)allocata. Se non c'è sufficiente quantità di memoria, viene restituito NULL. void *calloc( size_t n_elem, size_t elem_size ) void *realloc( void *p, size_t size )
Calloc e Realloc ... typedef struct { char nome[20]; char cognome[20]; int telefono; } persona; int matricola; } studente; .... persona *pp, *grp; studente *ps;
/* allocazione dinamica di una persona */ pp=(persona*)malloc(sizeof(persona)); pp->telefono=77; /* allocazione dinamica di 10 persone */ grp=(persona*)calloc(10,sizeof(persona)); if (!grp) printf("Out of memory"); Ho allocato 10 celle di memoria che conterranno strutture di tipo persona.
/* riallocazione dinamica di pp */ ps=(studente*)realloc(pp,sizeof(studente)); ps->matricola=888; printf("%d%d",ps->matricola,ps->telefono); ... Ho riallocato la cella di memoria che conteneva una struttura di tipo persona (puntata da pp).
Calloc e Realloc /* allocazione dinamica di 10 persone */ grp=(persona*)calloc(10,sizeof(persona)); if (!grp) printf("Out of memory"); /* allocazione dinamica di una persona */ pp=(persona*)malloc(sizeof(persona)); pp->telefono=77; /* riallocazione dinamica di pp */ ps=(studente*)realloc(pp,sizeof(studente)); ps->matricola=888; printf("%d%d",ps->matricola,ps->telefono); ...
Free void *free( void *p ) Rilascia una zona di memoria precedentemente allocata con una malloc, una calloc o una realloc. N.B.: andrebbe sempre usata prima della fine del programma su ogni variabile allocata dinamicamente. void *free( void *p )
Free ... typedef struct { char nome[20]; char cognome[20]; int telefono; } persona; .... /* definizione puntatori */ int *p, *q; persona *pp, *grp;
Free /* allocazione dinamica di memoria */ p=(int*)malloc(sizeof(int)); q=(int*)malloc(sizeof(*q)); pp=(persona*)malloc(sizeof(persona)); grp=(persona*)calloc(10,sizeof(persona)); ... /* rilascio della memoria allocata */ free(p); free(q); free(pp); free(gpr);
Le strutture dati Fino ad ora sono state introdotte strutture dati statiche (dimensione fissa a priori come vettori e record); in C ci sono strumenti per creare e gestire strutture dati dinamiche, la cui dimensione è una variabile gestita dall’utente. Un uso combinato di record e puntatori e una sapiente gestione dell’allocazione dinamica della memoria, permettono la costruzione e gestione di realtà complesse, di tipi di dato astratto.
Le Liste Concatenate Una lista concatenata è una collezione lineare di strutture connesse da puntatori, detti link (anelli o collegamenti), da cui il termine concatenata. Elemento Elemento Elemento
Le Liste Concatenate I nodi di una lista sono strutture ricorsive: una struttura contenente un membro di tipo puntatore, che fa riferimento a una struttura dello stesso tipo di quella in cui è contenuto. Elemento
Le Liste Concatenate Ci saranno sue nodi particolari: un puntatore al primo elemento e l’ultimo nodo punterà a NULL. Elemento
Le Liste Concatenate Ci saranno sue nodi particolari: un puntatore al primo elemento e l’ultimo nodo punterà a NULL. NULL Elemento
Le Liste Concatenate /**************** Direttive ***************/ #include <stdio.h> #include <stdlib.h> #define FALSE 0 #define TRUE 1
Le Liste Concatenate /************ Definizione tipi ************/ typedef int tipoelem; typedef struct elem *plist; typedef struct elem { int info; plist next; } elist; //typedef < tipo > < nuovo_nome_tipo >;
Le Liste Concatenate elist info plist info
Le Liste Concatenate /*********** Prototipi funzioni ***********/ /*restituisce la lista vuota*/ plist init(); /*verifica se una lista è vuota*/ int empty(plist); /*aggiunge un elemento in testa alla lista e restituisce la lista risultato*/ plist cons(plist, tipoelem); /*stampa tutta la lista*/ void stampa(plist);
Le Liste Concatenate /*********** Prototipi funzioni ***********/ /*restituisce il primo elemento della lista*/ tipoelem car(plist); /*toglie il primo elemento della lista e restituisce la lista risultato*/ plist cdr(plist);
Le Liste Concatenate temp plist init() NULL NULL l ? int empty(plist l)
Le Liste Concatenate /********** Definizione funzioni **********/ plist init() { plist temp; temp = NULL; return(temp); } int empty(plist l) { if (!l) return (TRUE); else return (FALSE);
Le Liste Concatenate plist cons(plist l, tipoelem e) temp l e tipoelem car(plist l) l info
Le Liste Concatenate plist cons(plist l, tipoelem e) { plist temp; temp = (plist)malloc(sizeof(elist)); temp->info = e; temp->next = l; return(temp); } tipoelem car(plist l) { return(l->info);
Le Liste Concatenate plist cdr(plist l) l temp temp l
Le Liste Concatenate plist cdr(plist l) l temp l
Le Liste Concatenate plist cdr(plist l) { plist temp; if (empty(l)) return(l); temp = l; l=l->next; free(temp); return(l); }
Le Liste Concatenate void stampa(plist l) l
Le Liste Concatenate void stampa(plist l) { printf("("); while (l) { printf("%d",l->info); printf(")"); l = l->next; } printf(")\n");
Le Liste Concatenate /********** Programma principale **********/ main() { int scelta; boolean continua=TRUE; tipoelem e; plist l; l=init();
Le Liste Concatenate while (continua) { /**** Stampa Menu Operazioni ****/ printf("--------Menu--------\n"); printf("1) Cons\n"); printf("2) Car\n"); printf("3) Cdr\n"); printf("4) Empty\n"); printf("5) Stampa\n"); printf("6) Esci\n"); printf("Scelta:"); scanf("%d",&scelta);
switch(scelta) { case 1: printf("Elemento:"); scanf("%d",&e); l=cons(l,e); break; case 2: if (empty(l)) printf("Vuota!\n"); else { printf("1^ elemento:"); printf("%d\n",car(l)); } break;
case 3: if (empty(l)) printf("Vuota!\n"); else l=cdr(l); break; case 4: else printf("Piena!\n"); break; case 5: stampa(l); break; default: continua=FALSE; break; }
APPUNTI SUL LINGUAGGIO C Corso di Algoritmi e strutture Dati APPUNTI SUL LINGUAGGIO C Strutture dati FINE