Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Linguaggio C Esempi
Linguaggio C Esempi
1
Premessa
Quanto avete sotto gli occhi non un manuale di linguaggio C, non serve per imparare a
programmare, non nemmeno un eserciziario completo ma semplicemente un insieme scelto e
annotato di esempi tratti dalle esercitazioni o proposti nei temi desame del corso di Informatica A
di Ingegneria Gestionale. Si sono cercati di evidenziare i punti che presentano le maggiori difficolt
e sono causa di errori ricorrenti per gli studenti, le cui osservazioni e domande, anche se talvolta a
loro insaputa, hanno consentito la raccolta. Si ringraziano anticipatamente quanti vorranno
contribuire a migliorarla.
Gli esercizi, suddivisi in base allargomento, sono riportati in forma completa, ad esempio nel caso
di una funzione viene inclusa anche la codifica del modulo che la richiama. Cos se da un lato non si
evita una certa ripetitivit e monotonia, dallaltro si facilita lestrazione del singolo esercizio e
quindi la modifica e il test. La suddivisione in parte arbitraria in quanto lo stesso esempio
potrebbe spesso trovarsi in capitoli diversi. Alle soluzioni compatte ed eleganti sono state preferite
quelle pi facilmente leggibili.
Per la codifica si usato il formato di carattere courier new, siete invitati ad usarla come punto
di partenza per le vostre variazioni e sperimentazioni: ogni linguaggio, naturale o artificiale che sia,
si apprende infatti soprattutto con la pratica. opinione di chi scrive che lo studio di un linguaggio
di programmazione (nella fattispecie il C) abbia anche un valore formativo nellapprendimento di
un metodo per affrontare i problemi che si presentano nella realt lavorativa.
Concludendo questo lavoro ha un duplice scopo: di fornire una serie di programmi completi come
input per esperimenti di laboratorio e di offrire alla vista un insieme di codifiche che sono pi facili
da leggersi da un foglio (o dallo schermo di un computer) che dalla lavagna durante le esercitazioni.
Dati strutturati
Uso di typedef
Il modo, a mio parere pi chiaro, per definire un tipo di dato fornito di struttura quello di usare le
parole chiave typedef struct. Il programma che segue definisce (nel file .h) alcuni tipi di
dati e alcune funzioni, che si possono pensare come modelli informatici di note entit ed operatori
della geometria elementare del piano. Si spera che la rappresentazione di un mondo geometrico che
tutti conoscono aiuti ad eliminare anche nella sua corrispondenza informatica ogni residua
ambiguit.
Per illustrare le due possibili modalit di chiamata, le funzioni che richiedono in input un triangolo
sono state implementate in modo differente e cio ad AreaT viene passato come parametro il
puntatore alla variabile triangolo, a Baricentro invece, viene passato il valore. Perch le
funzioni possano ritornare al chiamante non solo un numero ma anche un punto bisogna che questo
sia stato definito come un tipo dato.
Chi legge invitato ad accrescere questa libreria di funzioni a suo piacimento, si consiglia per
esempio di programmare il calcolo del punto di intersezione fra due rette che presenta qualche
interesse anche dal punto di vista informatico a causa dei diversi casi particolari che si possono
presentare.
Il file .c, esempio di come si pu costruire un programma di test della codifica di una funzione,
merita qualche commento, in quanto ha una struttura che ritroveremo in seguito. Esso composto
da un ciclo while che si interrompe quando viene fornita la risposta 0, le altre risposte fanno
prendere al processo diversi rami alternativi (comando switch case), ciascuno dei quali
corrisponde allesecuzione di una diversa funzione.
2
file: piano.h
/* costante booleana */
typedef enum {falso, vero} boolean;
if(temp1 == temp2)
all = vero;
return all;
}
3
punto_b.y = (tri.p1.y + tri.p2.y + tri.p3.y) / 3.0;
return (punto_b);
}
file: piano.c
#include <stdio.h>
#include "piano.h"
void main() {
punto punto1, punto2, punto3, punto4;
segmento segmento1;
triangolo triangolo1;
boolean allineamento;
int scelta = -1;
while(scelta != 0) {
printf("\n 0: per finire, 1: allineamento, 2: punto medio,\n 3: area
triangolo, 4: baricentro\n");
scanf("%d", &scelta);
switch(scelta) {
case 1:
printf("\nAscissa e ordinata del primo punto: ");
scanf("%f %f", &punto1.x, &punto1.y);
printf("\nAscissa e ordinata del secondo punto: ");
scanf("%f %f", &punto2.x, &punto2.y);
printf("\nAscissa e ordinata del terzo punto: ");
scanf("%f %f", &punto3.x, &punto3.y);
case 2:
printf("\nAscissa e ordinata del primo punto: ");
scanf("%f %f", &segmento1.p1.x, &segmento1.p1.y);
printf("\nAscissa e ordinata del secondo punto: ");
scanf("%f %f", &segmento1.p2.x, &segmento1.p2.y);
punto4 = PuntoM(segmento1);
StampaP(punto4);
break;
case 3:
printf("\nAscissa e ordinata del primo punto: ");
scanf("%f %f", &triangolo1.p1.x, &triangolo1.p1.y);
printf("\nAscissa e ordinata del secondo punto: ");
scanf("%f %f", &triangolo1.p2.x, &triangolo1.p2.y);
printf("\nAscissa e ordinata del terzo punto: ");
scanf("%f %f", &triangolo1.p3.x, &triangolo1.p3.y);
4
printf("\n area del triangolo: %5.2f", AreaT(&triangolo1));
break;
case 4:
printf("\nAscissa e ordinata del primo punto: ");
scanf("%f %f", &triangolo1.p1.x, &triangolo1.p1.y);
printf("\nAscissa e ordinata del secondo punto: ");
scanf("%f %f", &triangolo1.p2.x, &triangolo1.p2.y);
printf("\nAscissa e ordinata del terzo punto: ");
scanf("%f %f", &triangolo1.p3.x, &triangolo1.p3.y);
punto4 = Baricentro(triangolo1);
StampaP(punto4);
break;
}
}
Strutture di controllo
Uso di if condizionale
Il programma usoif illustra la condizione if, in esso listruzione scanf ritorna nella variabile
cc il numero di dati letti correttamente, basta perci inserire un valore che non sia intero per uscire
dal ciclo e terminare cos il ciclo di esecuzione del programma.
file: usoif.c
/* ordinamento di tre numeri (esempio di if) */
#include<stdio.h>
void main() {
int a, b, c, cc;
while(cc == 3)
{
if((a <= b) && (b <= c))
printf("\n %d %d %d", a, b, c);
else if((a <= c) && (c <= b))
printf("\n %d %d %d", a, c, b);
else if((b <= a) && (a <= c))
printf("\n %d %d %d", b, a, c);
else if((b <= c) && (c <= a))
printf("\n %d %d %d", b, c, a);
else if((c <= a) && (a <= b))
printf("\n %d %d %d", c, a, b);
else printf("\n %d %d %d", c, b, a);
printf("\nfornisci i tre interi da ordinare): ");
cc = scanf("%d %d %d", &a, &b, &c);
}
}
5
Ciclo for per gestire una matrice
Il ciclo for il modo pi semplice per percorrere una struttura a una o pi dimensioni (vettore,
matrice, array). Il programma che segue calcola e stampa i coefficienti dello sviluppo binomiale
fino alla decima potenza, usando il noto algoritmo che consiste nel calcolo dei coefficienti dello
sviluppo della potenza n-ma sommando a due a due da quelli della potenza n-1-ma. Il programma
composto da due coppie di cicli for annidati dei quali: quello esterno (indice i) visita le righe
della matrice M, quello interno percorre gli elementi della riga dal primo (j = 0), fino alla
diagonale principale (j = i).
file: usofor.c
/* uso del ciclo for con una matrice
calcolo dei coefficienti dello sviluppo della potenza di un binomio */
#include<stdio.h>
#define DIM 11
void main() {
long int M[DIM][DIM];
int i, j;
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
1 10 45 120 210 252 210 120 45 10 1
6
Cicli equivalenti
Si possono costruire strutture logiche equivalenti con cicli for, while e do-while, la scelta
dipende dalla convenienza e dal gusto personale. Nellesempio che segue viene riscritto in tre modi
lalgoritmo di calcolo della potenza di un numero (con esponente intero).
(prima di ogni insieme di istruzioni dovranno trovarsi le seguenti dichiarazioni e istruzioni:
if(espon < 0) {
espon = -espon;
base = 1.0 / base;
} )
Vettori e puntatori
Un puntatore un insieme di celle di memoria usate per contenere lindirizzo di un dato di un
particolare tipo, la sua definizione deve fare riferimento anche al tipo di dato.
Questo un punto molto importante da cui deriva la possibilit di unalgebra dei puntatori.
Allora (vedi il programma che segue):
char *p2; definisce p2 come indirizzo di una variabile carattere,
char *p1 = ABC; definisce p1 come indirizzo di una variabile carattere che viene
inizializzato con lindirizzo della stringa di caratteri ABC.
#include <stdio.h>
void main()
{
char *p1 = "ABC";
char *p2;
7
p2 = p1;
*p1 = 'X';
printf("p2: %s \n", p2); /* viene stampata la stringa XBC */
}
file: ricerca.c
/*****************************************************************
** Programma che ricerca la prima occorrenza di una specificata
** stringa di caratteri in un array di stringhe. versione 1
*****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 20
int main(void)
{
char *nomi[ ] = { "Priamo", "Achille", "Ettore", "Andromaca", NULL };
printf("nome: %s\n",nome_punt);
printf("nome %s%strovato\n", nuovo_nome,
(nome_punt == NULL) ? " non " : " ");
return (0);
} /* End of main */
/****************************************************************
** Funzione find_name. Questa funzione ricerca in un array di
** nomi un dato nome.
** Ritorna il puntatore al nome trovato o NULL se non e' stato
** trovato.
** array e' il puntatore al vettore di puntatori ai nomi
** esistenti
** stringa e' il puntatore alla stringa fornita (nuovo nome)
***************************************************************/
8
char * find_name(char **array, char *stringa)
{
for (; *array != NULL; array++) /* per ogni nome */
{
if (strcmp(*array, stringa) == 0)
return(*array);
}
return(*array); /* ritorna il puntatore */
} /* End of find_name */
file: pilacoda.c
#include<stdio.h>
#define MAX_SZ 5
typedef struct {
int Info[MAX_SZ];
int numelem;
} Pila;
9
typedef struct {
int Info[MAX_SZ];
int numelem;
int first;
} Coda;
10
Pila pila1;
Coda coda1;
pila1.numelem = 0;
coda1.numelem = 0;
InserInPila(&pila1,1);
StampaPila(&pila1);
InserInPila(&pila1,2);
StampaPila(&pila1);
InserInPila(&pila1,3);
StampaPila(&pila1);
printf("%4d\n",TogliDaPila(&pila1));
StampaPila(&pila1);
printf("\n");
InserInCoda(&coda1,10);
StampaCoda(&coda1);
InserInCoda(&coda1,11);
StampaCoda(&coda1);
InserInCoda(&coda1,12);
StampaCoda(&coda1);
printf("\n");
printf("%4d\n",TogliDaCoda(&coda1));
printf("\n");
StampaCoda(&coda1);
printf("\n");
printf("%4d\n",TogliDaCoda(&coda1));
printf("\n");
StampaCoda(&coda1);
printf("\n");
InserInCoda(&coda1,20);
InserInCoda(&coda1,21);
InserInCoda(&coda1,22);
InserInCoda(&coda1,23);
StampaCoda(&coda1);
printf("\n");
printf("%4d\n",TogliDaCoda(&coda1));
printf("\n");
printf("%4d\n",TogliDaCoda(&coda1));
printf("\n");
StampaCoda(&coda1);
Coda: 10
Coda: 10 11
Coda: 10 11 12
10
Coda: 11 12
11
Coda: 12
11
Coda: 12 20 21 22 23
12
20
Coda: 21 22 23
file: ctofar.c
#include <stdio.h>
void convert(double);
void main(int argc, char **argv)
{
double c_temp;
if (argc == 1) { /* ottieni la temperatura in Celsius da stdin */
printf("inserisci la temperatura in gradi Celsius: \n");
if (scanf("%lf", &c_temp) !=1) {
printf("temperatura invalida \n");
}
else {
convert(c_temp);
}
}
else { /* conversione della riga comandi */
int i;
for (i = 1; i < argc; ++i) {
if (sscanf(argv[i], "%lf", &c_temp) != 1)
printf("%s temperatura non valida\n", argv[i]);
else
convert(c_temp);
}
}
}
void convert(double c_temp) {
double f_temp = (c_temp * 9.0 / 5.0 + 32);
printf("%5.2f Celsius: %5.2f Fahrenheit\n", c_temp, f_temp);
}
12
32.00 Celsius: 89.60 Fahrenheit
51.00 Celsius: 123.80 Fahrenheit
-17.00 Celsius: 1.40 Fahrenheit
Visibilit e funzioni
file: visib.c
#include <stdio.h>
int i = 1; /* i definita al livello di file */
int main()
{
{
int i = 2, j = 3; /* i e j definite a
livello di blocco */
printf("%d\n%d\n", i, j); /* stampa 2, 3 */
{
int i = 0; /* i ridefinita in un blocco interno */
/* le definizioni precedenti di i sono nascoste */
printf("%d\n%d\n", i, j); /* stampa 0, 3 */
}
return 0;
Potenza
Ogni funzione vede le variabili che appartengono al suo ambiente locale cio quelle dichiarate
nella sua testata e nella sua parte dichiarativa e, quando rende il controllo al programma chiamante
(nella maggior parte dei casi il main), pu ritornare un valore mediante listruzione
return(..).
Perci, se la funzione di tipo algebrico e calcola un risultato dipendente da una serie di variabili
esterne, queste ultime sono generalmente passate come valori alla funzione che ottiene la soluzione
e la ritorna.
La funzione potenza calcola la potenza (intera) di un numero attraverso un ciclo for:
file: potenza.c
/* potenza di un numero */
13
#include<stdio.h>
ris = 1.0;
void main() {
float base;
int espon;
while(base > 0) {
printf("\nfornisci esponente (numero intero): ");
scanf("%d", &espon);
printf("\nrisultato : %f", potenza(base,espon));
printf("\nfornisci base (un numero negativo termina il programma): ");
scanf("%f", &base);
}
}
file: equaz2do.c
#include<stdio.h>
#include<math.h>
double x1,x2,Disc;
if (a==0)
printf("\n errore: equazione di primo grado");
else {
Disc=b*b-4*a*c;
if (Disc < 0)
printf("\n errore: radice complessa ");
else {
x1 = (-b+sqrt(Disc)) / (2.0*a);
x2 = (-b-sqrt(Disc)) / (2.0*a);
14
}
void main() {
double A, B, C;
printf("\nvalore di A, B e C: \n");
scanf("%lf %lf %lf", &A, &B, &C);
EquazioneSecondoGrado(A, B, C);
}
file: scambia.h
/* definizione del tipo dato numero complesso */
typedef struct {
double realp, imgp;
} complex;
/* funzioni di scambio fra due variabili (una funzione per tipo dato) */
15
}
file: scambia.c
/* programma di prova della funzione scambia */
#include<stdio.h>
#include"scambia.h"
void main() {
int unoi = 1, duei = 2;
char ac = 'a', bc = 'b';
float fla = 1.0100, flb = 2.0200;
double dba = 10.0030, dbb = 23.9950;
complex cx1, cx2;
cx1.realp = 1.2; cx1.imgp = 2.1;
cx2.realp = 91.2; cx2.imgp = 92.1;
printf("\n cx1: %lf %lf cx2: %lf %lf", cx1.realp, cx1.imgp, cx2.realp,
cx2.imgp);
scambiacx(&cx1,&cx2);
printf("\n cx1: %lf %lf cx2: %lf %lf", cx1.realp, cx1.imgp, cx2.realp,
cx2.imgp);
}
16
Scambio dei valori di due variabili (ver. 2)
Se si vuole costruire una funzione di scambio che operi su diversi tipi di dato bisogna passare ad
essa anche il tipo di dato interessato, in questo caso i puntatori passati devono essere modificati
attraverso loperatore di casting. I puntatori infatti sono puntatori a un particolare tipo di dato.
Ecco allora la rielaborazione del programma precedente, in cui il primo parametro passato alla
funzione un carattere che viene interpretato come il tipo del dato su cui operare. In questa
implementazione viene usato il costrutto di controllo switch - case.
file: scambiab.c
/* programma di prova della funzione scambia */
#include<stdio.h>
switch (tipo) {
case 'c':
tmpch = * (char *)pt1;
* (char *)pt1 = * (char *)pt2;
* (char *)pt2 = tmpch;
break;
case 'i':
tmpin = * (int *)pt1;
* (int *)pt1 = * (int *)pt2;
* (int *)pt2 = tmpin;
break;
case 'f':
tmpfl = * (double *)pt1;
* (double *)pt1 = * (double *)pt2;
* (double *)pt2 = tmpfl;
break;
case 'x':
tmpcx = * (complex *)pt1;
* (complex *)pt1 = * (complex *)pt2;
* (complex *)pt2 = tmpcx;
break;
default:
puts("tipo sconosciuto");
}
}
void main() {
int unoi = 111, duei = 222;
double unof = 10.10002, duef = 20.20001;
char ac = 'Y', bc = 'Z';
void *pt1, *pt2;
complex cx1, cx2;
cx1.realp = 1.2; cx1.imgp = 9.3;
cx2.realp = 8.2; cx2.imgp = 8.3;
17
printf("\n uno: %i due: %i ", unoi, duei);
pt1 = &unoi; pt2 = &duei;
scambia('i',pt1,pt2);
printf("\n uno: %i due: %i ", unoi, duei);
printf("\n cx1: %lf %lf cx2: %lf %lf", cx1.realp, cx1.imgp, cx2.realp,
cx2.imgp);
pt1 = &cx1; pt2 = &cx2;
scambia('x',pt1,pt2);
printf("\n cx1: %lf %lf cx2: %lf %lf", cx1.realp, cx1.imgp, cx2.realp,
cx2.imgp);
Dati strutturati
Ricorsivit
Molti algoritmi possono essere risolti in modo ricorsivo. Secondo questo paradigma il problema di
partenza pu essere risolto attraverso la soluzione del problema stesso su un sottoinsieme dei dati e
combinando i risultati ottenuti. Lalgoritmo richiama allora se stesso per una esecuzione interna alla
precedente, finch non si verifichi una condizione di arresto.
Vengono ora riportati alcuni algoritmi codificati sia in modalit iterativa sia ricorsiva cos che il
confronto sia pi facile, pi oltre si affronteranno in modo analogo problemi che implicano strutture
dinamiche che sono ricorsive anche nella loro stessa definizione.
file: MCDiv.c
/*************************************************************
** Massimo Comun Divisore fra due numeri interi **
** algoritmo di Euclide: **
18
** il MCD fra due numeri e' il MCD fra il minore dei due **
** e la loro differenza **
*************************************************************/
#include<stdio.h>
/* codifica ricorsiva */
int MCDric(int a, int b)
{
int differenza, minore;
if(a == b)
return (b);
else {
differenza = a>b ? a-b : b-a;
minore = a>b ? b : a;
return (MCDric(differenza,minore));
};
/* codifica iterativa */
int MCDite(int a, int b)
{
while (a != b) {
if (a > b)
a = a - b;
else
b = b - a;
}
return (b);
void main ()
{
int x, y;
printf("introdurre due numeri interi\n");
scanf("%d %d", &x, &y);
printf("Il Massimo Comun Divisore fra %d e %d e' %d\n", x, y, MCDric(x,y));
printf("Il Massimo Comun Divisore fra %d e %d e' %d\n", x, y, MCDite(x,y));
}
19
Media ricorsiva
Anche la media pu essere ottenuta ricorsivamente osservando che:
X i X i 1
X i X i 1
, i 1, X0 0
i
file: mediar.c
#include <stdio.h>
float Media(float elementi[], int num_elementi)
/* funzione di calcolo della media in modo ricorsivo
** bisogna fornire l'indirizzo del vettore che contiene i valori
** e la sua lunghezza */
{
float med,med1;
if(num_elementi == 1)
med=*elementi;
else
{
med1=Media(elementi + 1, num_elementi - 1);
med=med1 + ((*elementi - med1) / num_elementi);
}
return med;
}
void main()
{
int cont, num_elem;
float media, elementi[50];
printf("inserisci il numero di elementi dei quali vuoi fare la media: ");
scanf("\n%d", &num_elem);
for(cont=0; cont<num_elem; cont++)
{
printf("Inserisci numero: \n");
scanf("%f",&elementi[cont]);
}
Polinomio
Per un polinomio di grado n nella variabile x di coefficienti ci, vale la seguente eguaglianza:
file: poli.c
/* calcolo ricorsivo di un polinomio */
#include <stdio.h>
#define MAX_SZ 10
20
degli n+1 coefficienti del polinomio, il suo grado n */
float Poli(float x, float * c, int n) {
float Poli_val = 0.0;
if (n >= 0)
Poli_val = *c + x * Poli(x, c + 1, n - 1);
return (Poli_val);
}
void main () {
float coeff[MAX_SZ], polinomio, x;
int i, n, cont;
while(cont == 1){
printf("\nInserisci i %i coefficienti\n", n + 1);
polinomio = Poli(x,coeff,n);
Algoritmi vari
Ricerca binaria
Lalgoritmo ricerca un dato in un insieme ordinato, consiste nel posizionarsi al centro della
sequenza e individuare in quale delle due met si trova eventualmente il valore cercato poi si
prosegue in modo analogo nella nuova met, fino al successo o allesaurimento dei dati. Se N il
numero degli elementi, il numero di ricerche allora proporzionale a log2(N).
La funzione ricbin ricerca un numero intero x nella porzione del vettore v compresa fra gli
indici i e j. Essa ritorna al programma chiamante 1 se la ricerca ha avuto successo, 0 in caso
contrario. Non difficile riscriverla in modo ricorsivo.
file: binr2.c
#include <stdio.h>
#define N 10
int ricbin(int x, int v[ ], int i, int j) {
int k;
if (i > j)
return (0);
do {
k = (i + j) / 2;
if (x < v[k])
j = k - 1;
else
21
i = k + 1;
} while ((i <= j) && (v[k] != x));
return (x == v[k]);
}
int main(void)
{
int vect[N], i, sx = 0, dx = 9, n, x;
printf("introdurre una sequenza ordinata di %d numeri\n", N);
for (n = 0; n < N ; n++ ) {
scanf("%d", &x);
vect[n] = x;
} /* endfor */
printf("introdurre il numero da cercare \n");
scanf("%d", &x);
i = ricbin(x,vect,sx,dx);
if (i != 0) printf("trovato %d\n", x);
else printf("%d non trovato\n", x);
return (0);
}
file: strinv.c
#include<stdio.h>
#define MAX_LN 30
int main() {
char parola[MAX_LN];
int lung;
int inverti(char parola[]);
return 0;
}
return (l + 1);
}
file:string0.c
#include<stdio.h>
#include<stdlib.h>
void main() {
char stringa[30];
scanf("%s", stringa);
printf("\n%s", stringa);
ConsumaDoppi(stringa);
printf("\n%s ", stringa);
}
23
Il programma conta1 legge un file di testo e conta i digit numerici e quelli alfabetici. Si noti che
i caratteri sono numeri interi perci non ci si deve meravigliare se valgono gli operatori aritmetici
e di confronto.
file: conta1.c
#include <stdio.h>
/*conta i caratteri alfabetici e numerici di un testo*/
void main()
{
int c, i, altri = 0, spazi = 0;
int ndigit[10];
int nalfab[26];
for (i = 0; i < 10; i++)
ndigit[i] = 0;
for (i = 0; i < 26; i++)
nalfab[i] = 0;
while ((c = getchar()) != EOF)
if (c >= '0' && c <= '9')
++ndigit[c - '0'];
else if(c == ' ')
++spazi;
else if((c >= 'A') && (c <= 'Z'))
++nalfab[c - 'A'];
else if((c >= 'a') && (c <= 'z'))
++nalfab[c - 'a'];
else ++altri;
printf("\nnumeri\n");
for (i = 0; i < 10; ++i)
printf(" %3d", i);
printf("\n");
for (i = 0; i < 10; ++i)
printf(" %3d", ndigit[i]);
printf("\nlettere\n");
for (i = 0; i < 26; ++i)
printf(" %3c", 'A' + i);
printf("\n");
for (i = 0; i < 26; ++i)
printf(" %3d", nalfab[i]);
printf("\n spazi = %d, altri = %d", spazi, altri);
}
numeri
0 1 2 3 4 5 6 7 8 9
16 4 4 4 0 0 4 0 0 1
lettere
A B C D E F G H I J K L M N O P Q R S T
U V W X Y Z
30 6 19 13 21 26 5 3 70 0 0 17 3 34 11 13 0 25 10 30
4 1 1 0 0 6
spazi = 193, altri = 313
24
Strutture dinamiche
Lintroduzione di una struttura dinamica permette una maggiore flessibilit nelluso della memoria
che pu essere allocata e liberata in funzione della necessit.
Liste
Una lista composta da una successione di elementi, quindi perch possa essere percorsa
interamente almeno in una direzione, bisogna che ogni elemento contenga lindirizzo di quello
successivo. La definizione dellelemento di lista ricorsiva: contiene il puntatore a un dato dello
stesso tipo. Ogni lista deve avere un inizio e una fine, lultimo elemento si distingue per il fatto che
lindirizzo del prossimo elemento la costante NULL (= fine lista); il primo elemento viene invece
raggiunto attraverso un puntatore, che si usa chiamare testa della lista, ed da considerarsi parte
integrante della lista stessa. Una funzione progettata per gestire una lista deve sempre ricevere come
parametro il puntatore ad essa. Se la funzione pu modificare il puntatore perch, per esempio,
inserisce in testa un nuovo elemento, necessario che riceva come parametro un doppio puntatore.
Infatti con questa tecnica la nostra funzione in grado di modificare la testa della lista anche se si
trova nellarea di memoria del programma chiamante.
Il programma listai che composto dal file .h che contiene la definizione dellelemento pi
semplice che si possa pensare, composto da un dato intero e da un puntatore e qualche esempio di
funzione di gestione della lista. Di alcune di queste funzioni vengono presentate implementazioni
diverse. Si osservi che, come gi notato, le funzioni che leggono la lista ma la lasciano inalterata (ad
esempio quelle di stampa) possono ricevere in input il puntatore alla lista, quelle che ne modificano
la struttura hanno invece come parametro in ingresso il puntatore al puntatore alla lista.
Le funzioni sono molto semplici, si evitato di allocare e rilasciare la memoria perci nel main
(file .c), - da usarsi per esperimenti - gli elementi sono gi predefiniti staticamente. In qualche
caso la versione ricorsiva di una funzione quella pi immediata e naturale. Si confronti, per
esempio, la codifica delle funzioni di stampa e si osservi come sia possibile, nella versione
ricorsiva, cambiare lordine di stampa semplicemente invertendo lordine di due istruzioni. Non
sarebbe altrettanto facile in quella iterativa.
file: listai.h
/* definizione dell'elemento di lista di interi */
typedef struct EL {
int Info;
struct EL *next;
} ElmLista, *Lista;
25
if(l) {
StampaInversa(l->next);
printf("%i\n",l->Info);
}
}
/* inserimento in testa */
void InserisciInTesta(Lista *lp, ElmLista *elp) {
elp->next = *lp;
*lp = elp;
}
if (*lp == NULL) {
*lp = elp; /* fine della lista */
elp->next = NULL;
}
else {
InserisciInCodaR(&(*lp)->next, elp);
}
26
void InserisciInOrdine(Lista *lp, ElmLista *elp) {
ElmLista *elementoCorrente, *elementoPrecedente;
elementoCorrente = *lp;
elementoPrecedente = NULL;
while (elementoCorrente != NULL &&
elementoCorrente->Info < elp->Info) {
elementoPrecedente = elementoCorrente;
elementoCorrente = elementoCorrente->next;
}
elp->next = elementoCorrente;
if (elementoPrecedente != NULL)
elementoPrecedente->next = elp; /* caso di lista non vuota */
else *lp = elp;
}
file: listai.c
#include <stdio.h>
#include "listai.h"
void main() {
ElmLista Elm1, Elm2, Elm3, Elm4, Elm5, Elm6, Elm0, *PElm;
Lista Lista1;
Lista1 = &Elm1;
Elm1.Info = 1;
Elm1.next = &Elm2;
Elm2.Info = 2;
Elm2.next = &Elm3;
Elm3.Info = 3;
Elm3.next = &Elm5;
Elm5.Info = 5;
Elm5.next = NULL;
Elm4.Info = 4;
Elm6.Info = 6;
Elm0.Info = 0;
StampaLista(Lista1);
printf("\n");
InserisciInOrdine(&Lista1,&Elm4);
InserisciInOrdine(&Lista1,&Elm6);
InserisciInOrdine(&Lista1,&Elm0);
StampaLista(Lista1);
printf("\n");
StampaInversa(Lista1);
printf("\n");
/*
printf("\n %i \n ",(*TogliDaTesta(&Lista1)).Info);
printf("\n");
StampaLista(Lista1);
printf("\n");
27
printf("\n %i \n ",TogliDaTesta(&Lista1)->Info);
printf("\n");
StampaLista(Lista1);
InserisciInCoda(&Lista1,&Elm4);
StampaLista(Lista1);
printf("\n");
InserisciInCodaR(&Lista1,&Elm0);
StampaLista(Lista1);
printf("\n");
printf("\n %i ",LungListaI(Lista1));
printf("\n %i ",LungListaR(Lista1));
printf("\n %i ",LungListaI(NULL));
printf("\n %i ",LungListaR(NULL));
StampaDiretta(Lista1);
printf("\n");
StampaInversa(Lista1);
printf("\n");
*/
0
1
2
3
4
5
6
6
5
4
3
2
1
0
file: pila.h
typedef struct _elPila
{
int Info;
struct _elPila *prossimo;
} elPila, *Pila;
nuovoElemento->Info = el;
nuovoElemento->prossimo = p;
p = nuovoElemento;
return(p);
}
void StampaPila(Pila p)
{
if (p == NULL) {
printf("la pila non ha elementi\n");
return;
}
while (p != NULL)
{
printf("%i\n", p->Info);
p = p->prossimo;
}
}
file: pila.c
#include <stdio.h>
#include <stdlib.h>
#include "pila.h"
29
void main()
{
Pila p = NULL;
int el;
int scelta = -1;
while(scelta != 0) {
printf("\n 0 per uscire, 1 per inserire, 2 per togliere, 3 per
visualizzare\n");
scanf("%d", &scelta);
switch(scelta) {
case 1:
printf("inserire un elemento: ");
scanf("%d", &el);
p = Inserisci(p, el);
if (p == NULL) printf("inserimento non riuscito");
break;
case 2:
p = Togli(p, &el);
if (p != NULL) printf("%i ",el);
break;
case 3:
StampaPila(p);
break;
}
}
file: coda.h
typedef struct _elCoda
{
int Info;
struct _elCoda *prossimo;
} elCoda, *Coda;
nuovoElemento->Info = el;
/* se Coda vuota */
if (pc == NULL)
nuovoElemento->prossimo = nuovoElemento;
else { /* se esistono elementi */
nuovoElemento->prossimo = pc->prossimo;
pc->prossimo = nuovoElemento;
}
return(nuovoElemento);
30
}
file: coda.c
#include <stdio.h>
#include <stdlib.h>
#include "pila.h"
void main()
{
Pila p = NULL;
int el;
int scelta = -1;
while(scelta != 0) {
printf("\n 0 per uscire, 1 per inserire, 2 per togliere, 3 per
visualizzare\n");
scanf("%d", &scelta);
switch(scelta) {
case 1:
printf("inserire un elemento: ");
scanf("%d", &el);
p = Inserisci(p, el);
if (p == NULL) printf("inserimento non riuscito");
break;
case 2:
31
p = Togli(p, &el);
if (p != NULL) printf("%i ",el);
break;
case 3:
StampaPila(p);
break;
}
}
Alberi
Dopo la lista la struttura dinamica pi semplice lalbero binario, struttura gerarchica in cui ogni
elemento pu avere ha due figli, sinistro e destro, a partire dalla radice fino alle foglie (che
terminano il ramo, non avendo pi figli. Un albero binario pu essere visitato in modi diversi.
Lesempio che segue (file albero2.c) mostra la costruzione (funzione: addtree) di un albero
di stringhe (elemento dellalbero: Treenode), la sua visita e stampa in ordine alfabetico (funzione:
treeprint) e la stampa della struttura (funzione: StampaAlbero).
Le funzioni fin qui elencate sono ricorsive, oltre a queste nel programma ne sono codificate altre di
servizio che servono per copiare le stringhe e allocare le aree di memoria necessarie. Essendo state
codificate dopo il modulo main, devono essere dichiarate prima del loro uso.
Viene poi mostrato un esempio di esecuzione del programma con come input la prima terzina del
Purgatorio di Dante (input in carattere corsivo).
Per aggiungere un nuovo elemento si parte dalla radice muovendosi a sinistra o a destra in funzione
del fatto che la parola si trovi in ordine alfabetico prima o dopo della radice, Se abbiamo raggiunto
la fine del ramo, il nuovo elemento viene aggiunto come foglia in quella posizione; in caso
contrario lelemento incontrato la radice del sottoalbero dal quale si riparte con lo stesso
procedimento.
Per percorrere in ordine alfabetico lalbero cos costruito bisogna raggiungere la foglia pi a sinistra
e seguirlo a ritroso stampando lelemento radice di ogni sottoalbero dopo gli elementi del ramo
sinistro e prima di quelli del destro. Come sempre il modo migliore per capire lalgoritmo quello
di operare su un caso pratico studiando la traccia del programma.
Infine, per comprendere meglio la stampa della struttura dellalbero costruito, occorre ruotare di 90
gradi in senso orario il foglio con loutput, allora si legger agevolmente che:
per la radice dellalbero,
correr e vele sono i figli della radice di sinistra e di destra rispettivamente,
acque e migliori sono figli di correr,
vele ha solo il figlio di sinistra (se),
acque ha i figli: a e alza,
ecc.
file: albero2.c
/* esempio di gestione albero binario */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define MAXWORD 30
typedef struct tnode *Treeptr;
typedef struct tnode { /* nodo dell'albero */
32
char *word; /* puntatore al testo */
int count; /* numero di occorrenze */
Treeptr left, right; /* figli di sinistra e di destra */
} Treenode;
root = NULL;
printf("\ninserisci una parola alla volta <END termina la sequenza>\n");
scanf("%s", word);
while(*word != *fine)
{
root = addtree(root, word);
scanf("%s", word);
}
printf("\n ALBERO ORDINATO\n");
treeprint(root);
printf("\n");
printf("\n STRUTTURA DELL'ALBERO\n");
printf("\n");
StampaAlbero(root, 1);
return 0;
}
33
treeprint (p->left);
printf("%s (%d)\n", p->word, p->count);
treeprint(p->right);
}
}
/* crea un tnode */
Treenode *talloc(void)
{
return (Treeptr)malloc(sizeof(Treenode));
}
STRUTTURA DELL'ALBERO
vele
si'
se'
per
ormai
navicella
mio
migliori
mar
le
lascia
la
ingegno
dietro
del
crudele
correr
che
alza
acque
a
Input Output
file: operio.c
/* il programma richiede la successione di parole con cui viene popolato il file
prova.txt. L'elenco viene terminato quando viene inserito il carattere '#'
a inserimento ultimato vengono richieste due parole, la prima viene cercata
nel file e, se trovata, sostituita con la seconda */
#include<stdio.h>
#include<string.h>
#define LUNG 20
void main() {
char filename[] = "prova.txt";
char parola[LUNG], parolaDaTrovare[LUNG], parolaNuova[LUNG];
int n;
long pos = 0L;
35
FILE *fp;
36