You are on page 1of 36

Alcuni esercizi di programmazione in linguaggio C

Indice degli argomenti


Alcuni esercizi di programmazione in linguaggio C.......................................................................1
Indice degli argomenti......................................................................................................................1
Premessa...........................................................................................................................................2
Dati strutturati...................................................................................................................................2
Uso di typedef..............................................................................................................................2
Strutture di controllo........................................................................................................................5
Uso di if condizionale...................................................................................................................5
Ciclo for per gestire una matrice..................................................................................................6
Cicli equivalenti...........................................................................................................................7
Vettori e puntatori.............................................................................................................................7
Ricerca di una stringa in un vettore di stringhe (vers. 1).............................................................8
Ricerca di una stringa in un vettore di stringhe (vers. 2 della sola funzione)..............................9
Strutture pila e coda (statiche) realizzate con un vettore..............................................................9
Passaggio di parametri al main...................................................................................................12
Visibilit e funzioni........................................................................................................................13
Visibilit delle variabili in blocchi annidati...............................................................................13
Potenza.......................................................................................................................................13
Calcolo delle radici di unequazione di 2 grado.......................................................................14
Scambio dei valori di due variabili (ver. 1)................................................................................15
Scambio dei valori di due variabili (ver. 2)................................................................................16
Dati strutturati.................................................................................................................................18
Ricorsivit......................................................................................................................................18
Massimo Comun Divisore..........................................................................................................18
Media ricorsiva...........................................................................................................................20
Polinomio...................................................................................................................................20
Algoritmi vari.................................................................................................................................21
Ricerca binaria............................................................................................................................21
Operazioni diverse su strighe di caratteri...................................................................................22
Strutture dinamiche........................................................................................................................25
Liste............................................................................................................................................25
Pile e code (strutturate come lista).............................................................................................28
Alberi..........................................................................................................................................32
Input Output...................................................................................................................................35
Esempio di scrittura, lettura e aggiornamento di un file............................................................35

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;
/* punto nel piano */
typedef struct {
float x;
float y;
} punto;
/* retta (o segmento) per due punti */
typedef struct {
punto p1;
punto p2;
} segmento;
/* triangolo (individuato dai suoi vertici)
typedef struct {
punto p1;
punto p2;
punto p3;
} triangolo;

*/

/* verifica se tre punti sono allineati */


boolean Ver_All(punto p1, punto p2, punto p3)
{
boolean all = falso;
float temp1, temp2;
temp1 = (p1.x - p2.x) * (p1.y - p3.y);
temp2 = (p1.x - p3.x) * (p1.y - p2.y);

if(temp1 == temp2)
all = vero;
return all;

/* calcolo del punto medio di un segmento */


punto PuntoM (segmento seg_)
{
punto punto_medio;
punto_medio.x = (seg_.p1.x + seg_.p2.x) / 2.0;
punto_medio.y = (seg_.p1.y + seg_.p2.y) / 2.0;
return (punto_medio);
}
/* calcolo dell'area (con segno) di un triangolo (passato mediante puntatore) */
float AreaT (triangolo *ptri)
{
return((ptri->p1.x * ptri->p2.y +
ptri->p3.x * ptri->p1.y +
ptri->p2.x * ptri->p3.y ptri->p3.x * ptri->p2.y ptri->p1.x * ptri->p3.y ptri->p2.x * ptri->p1.y) / 2.0);
}
/* calcolo del baricentro di un triangolo (passato come valore) */
punto Baricentro (triangolo tri)
{
punto punto_b;
punto_b.x = (tri.p1.x + tri.p2.x + tri.p3.x) / 3.0;

punto_b.y = (tri.p1.y + tri.p2.y + tri.p3.y) / 3.0;


return (punto_b);
}
/* stampa delle coordinate di un punto */
void StampaP (punto P)
{
if ((P.x != NULL) && (P.y != NULL))
printf("\n x: %5.2f
y: %5.2f\n", P.x, P.y);
else
printf("\n punto invalido o inesistente\n");
}

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);
allineamento = Ver_All(punto1, punto2, punto3);
if(allineamento == vero)
printf("\ni punti sono allineati");
else
printf("\ni punti non sono allineati");
break;
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
scanf("%f %f", &triangolo1.p1.x,
printf("\nAscissa e ordinata del
scanf("%f %f", &triangolo1.p2.x,
printf("\nAscissa e ordinata del
scanf("%f %f", &triangolo1.p3.x,

primo punto:
");
&triangolo1.p1.y);
secondo punto: ");
&triangolo1.p2.y);
terzo punto: ");
&triangolo1.p3.y);

printf("\n area del triangolo: %5.2f", AreaT(&triangolo1));


break;
case 4:
printf("\nAscissa e ordinata del
scanf("%f %f", &triangolo1.p1.x,
printf("\nAscissa e ordinata del
scanf("%f %f", &triangolo1.p2.x,
printf("\nAscissa e ordinata del
scanf("%f %f", &triangolo1.p3.x,

primo punto:
");
&triangolo1.p1.y);
secondo punto: ");
&triangolo1.p2.y);
terzo punto: ");
&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;
printf("\nfornisci i tre interi da ordinare):
cc = scanf("%d %d %d", &a, &b, &c);

");

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);
}
}

");

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;
/* costruzione della matrice */
for(i = 0; i < DIM; i++)
for(j = 0; j < i+1; j++) {
if((j == 0)||(j == i))
M[i][j] = 1;
else
M[i][j] = M[i-1][j-1] + M[i-1][j];
}
/* stampa della matrice */
for(i = 0; i < DIM; i++) {
for(j = 0; j < i+1; j++)
printf(" %4ld ", M[i][j]);
printf("\n");
}
}

Output della esecuzione di usofor:


1
1
1
1
1
1
1
1
1
1
1

1
2
3
4
5
6
7
8
9
10

1
3
6
10
15
21
28
36
45

1
4
10
20
35
56
84
120

1
5
15
35
70
126
210

1
6
21
56
126
252

1
7
28
84
210

1
8
36
120

1
9
45

1
10

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:
float base, ris;
int espon;
if(espon < 0) {
espon = -espon;
base = 1.0 / base;
}
)

/* (1) ciclo for */


for(ris = 1.0; espon > 0; espon--)
ris = ris * base;
/* fine ciclo for */
/* (2) ciclo while */
ris = 1.0;
while(espon > 0)
{
ris = ris * base;
espon--;
}
/* fine ciclo while */
/* (3) ciclo do while */
ris = 1.0;
if (espon)
{
do
{
ris = ris * base;
espon--;
} while(espon > 0);
}
/* fine ciclo do while */

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;

p2 = p1;
*p1 = 'X';
printf("p2: %s \n", p2); /* viene stampata la stringa XBC */

Ricerca di una stringa in un vettore di stringhe (vers. 1)


Il programma contiene il vettore di puntatori char *nomi[ ] inizializzato con indirizzi di
stringhe gi definite, lultimo indirizzo contiene NULL per segnalare la fine del vettore degli
indirizzi. La funzione find_name ricerca una stringa passata fra quelle puntate.
Nella seconda versione la funzione identica ma ha una forma diversa, invece di eseguire la somma
direttamente sul puntatore, viene utilizzato un indice.
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

char * find_name(char **, char *);


int main(void)
{
char *nomi[ ] = { "Priamo", "Achille", "Ettore", "Andromaca", NULL };
/*
**
**
**
**
**

nomi: doppio puntatore a carattere


nomi[ ]: vettore di puntatori a carattere
nomi[0][0]: contiene 'P'
nomi[1][1]: contiene 'c'
nomi[3][0]: contiene 'A'
(ogni stringa ha lunghezza diversa)

**
**
**
**
**
*/

char nuovo_nome[SIZE], *nome_punt;


printf("inserisci il nome da cercare.\n");
scanf("%s", nuovo_nome);
nome_punt = find_name(nomi, nuovo_nome);
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)
***************************************************************/

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 */

Ricerca di una stringa in un vettore di stringhe (vers. 2 della sola funzione)


char * find_name(char *array[ ], char *stringa)
{
int i;
for (i = 0; array[i] != NULL; i++)
/* per ogni nome */
{
if (strcmp(array[i], stringa) == 0)
return(array[i]);
}
return(NULL);
/* ritorna il puntatore
*/
} /* End of find_name */

Strutture pila e coda (statiche) realizzate con un vettore


Pila e coda sono strutture composte da componenti ordinati, perci naturalmente modellabili da
vettori, nellesempio si fa riferimento a numeri interi, ma pu essere usato ogni altro tipo di dato
eventualmente fornito di una sua ulteriore struttura interna. Un vettore un oggetto statico cio non
non possibile modificare dinamicamente la quantit di memoria, fissata in fase di compilazione,
che limita il numero massimo di elementi contenibili.
In una pila o coda gli elementi vengono inseriti e tolti uno alla volta, con questa differenza: nella
pila lultimo elemento inserito il primo che viene tolto (Last In First Out), nella coda gli elementi
vengono tolti seguendo lordine di arrivo (First In First Out). Nel primo caso allora basta conoscere
quanti sono gli elementi contenuti per sapere la posizione su cui operare, nellaltro bisogna invece
conservare la posizione sia dellultimo inserito sia di quello che, fra i presenti, stato inserito per
primo.
Per la coda si pone anche il problema di riutilizzare lo spazio: quando stata usata lultima
posizione del vettore (quella cui corrisponde lindice pi elevato) bisogna poter tornare a riusare la
prima posizione, se libera. Il calcolo della posizione quindi basato su una funzione ciclica (la
divisione in modulo per la lunghezza del vettore).
Le caratteristiche descritte sono rispecchiate pi sotto nelle definizioni dei tipi dato pila e coda e
nelle funzioni che gestiscono questi oggetti. Come nei casi gi incontrati il main contiene una
possibile codifica per eseguire le funzioni.
file: pilacoda.c
#include<stdio.h>
#define MAX_SZ 5
typedef struct {
int Info[MAX_SZ];
int numelem;
} Pila;

typedef struct {
int Info[MAX_SZ];
int numelem;
int first;
} Coda;
int InserInPila(Pila *p, int elm) {
int ret = NULL;
if (p->numelem < MAX_SZ) {
p->Info[p->numelem++] = elm;
ret = 1;
}
return (ret);
}
int TogliDaPila(Pila *p) {
int ret = NULL;
if (p->numelem > 0)
ret = p->Info[--(p->numelem)];
return (ret);
}
void StampaPila(Pila *p) {
int i;
printf("Pila: ");
for(i = 0; i < p->numelem; i++)
printf("%4d", p->Info[i]);
printf("\n");
}
int InserInCoda(Coda *c, int elm) {
int i, ret = NULL;
if(c->numelem < MAX_SZ) {
if(c->numelem == 0)
c->first = 0;
i = (c->first + c->numelem++) % MAX_SZ;
c->Info[i] = elm; ret = 1;
}
return (ret);
}
int TogliDaCoda(Coda *c) {
int reso = NULL;
if(c->numelem != 0) {
reso = c->Info[c->first];
c->numelem--;
c->first = (c->first + 1) % MAX_SZ;
}
return (reso);
}
void StampaCoda(Coda *c) {
int i,k;
printf("Coda: ");
for(i = 0;i < c->numelem; i++) {
k = (c->first + i) % MAX_SZ;
printf("%4d", c->Info[k]);
}
printf("\n");
}
void main() {

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);
}

Output della esecuzione di pilacoda:


Pila:
Pila:
Pila:
3
Pila:

1
1
1

2
2

Coda:
Coda:
Coda:

10
10
10

11
11

11

12

12

10
Coda:
11
Coda:

12

11

Coda:

12

20

21

21

22

23

22

23

12
20
Coda:

Passaggio di parametri al main


Anche il modulo main una funzione che riceve sempre due parametri in formato standard: un
intero e un puntatore a un vettore di puntatori a stringhe di caratteri (che dichiariamo nella testata
del main rispettivamente int argc, char **argv ). La prima stringa sempre il nome del
programma eseguibile, il parametro intero la lunghezza del vettore, le altre stringhe indirizzate dal
vettore sono i parametri che vengono passati al programma nel comando di esecuzione. Ovviamente
se non vengono passati parametri la variabile intera contiene 1.
Lesempio riguarda la conversione di temperature da gradi Celsius a Fahrenheit, le temperature
possono essere passate come parametri di esecuzione o da tastiera alla richiesta.
Listruzione sscanf legge una stringa alla volta dalla memoria convertendone il formato in
double.
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);
}

Output della esecuzione di:


ctofar 0 4 15 32 51 17
0.00 Celsius: 32.00 Fahrenheit
-4.00 Celsius: 24.80 Fahrenheit
15.00 Celsius: 59.00 Fahrenheit

12

32.00 Celsius: 89.60 Fahrenheit


51.00 Celsius: 123.80 Fahrenheit
-17.00 Celsius: 1.40 Fahrenheit

Visibilit e funzioni
Visibilit delle variabili in blocchi annidati
Le variabili definite in un blocco (insieme di comandi racchiusi da { }) sono visibili solo allinterno
di quel blocco perci, se in un blocco annidato in un altro viene definita una variabile con un nome
gi usato, questa una nuova variabile, accessibile solo allinterno di questultimo. Il programma
seguente mostra tale situazione. Attenzione quindi ai nomi delle variabili e a dove sono posizionate!
file: visib.c

#include <stdio.h>
int i = 1;

/* i definita al livello di file */

int main()
{
printf("%d\n", i);

/* stampa 1 */

{
int i = 2, j = 3;
printf("%d\n%d\n", i, j);
{

/* i e j definite a
livello di blocco */
/* 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 */

}
printf("%d\n", i);

/* stampa

2 */

}
printf("%d\n", i);

/* stampa 1 */

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>
float potenza(float base, int espon){
float ris;
if(espon < 0) {
espon = -espon;
base = 1.0 / base;
}
ris = 1.0;
for(; espon > 0; espon--)
ris = ris * base;
return(ris);
}
void main() {
float base;
int espon;
printf("\nfornisci base (un numero negativo termina il programma): ");
scanf("%f", &base);
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
scanf("%f", &base);
}

programma): ");

Calcolo delle radici di unequazione di 2 grado


Eccone ora una funzione nota: calcolo delle radici di unequazione di 2 grado noti i coefficienti. Le
radici sono generalmente 2 e la funzione di tipo void, non ritorna alcun valore al programma
chiamante ma stampa direttamente i risultati.
file: equaz2do.c
#include<stdio.h>
#include<math.h>
void EquazioneSecondoGrado(double a, double b, double c) {
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);

printf("\n x1 = %g", x1);


printf("\n x2 = %g", x2);

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);
}

Scambio dei valori di due variabili (ver. 1)


Unoperazione che viene eseguita frequentemente (necessaria ad esempio quando si esegue
lordinamento di un insieme di elementi) quella di effettuarne uno scambio fra due valori. Dal
momento che non esiste unistruzione elementare che operi uno scambio, bisogna eseguirlo in tre
passi appoggiandosi a una variabile intermedia. Se lo scambio viene fatto da una funzione, perch il
risultato sia permanente, questa deve operare nello spazio in cui sono definite le variabili, perci
deve accedere alle variabili mediante puntatori.
La prima soluzione presentata mostra tale tecnica, si noti anche che non essendo definito nel
linguaggio il dato numero complesso, questo viene definito dallutente come tipo derivato (il
numero complesso una coppia ordinata di reali).
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) */
void scambiach(char *pt1, char *pt2) {
char tmp;
tmp = *pt1;
*pt1 = *pt2;
*pt2 = tmp;
}
void scambiain(int *pt1, int *pt2) {
int tmp;
tmp = *pt1;
*pt1 = *pt2;
*pt2 = tmp;
}
void scambiafl(float *pt1, float *pt2) {
float tmp;
tmp = *pt1;
*pt1 = *pt2;
*pt2 = tmp;
}
void scambiadb(double *pt1, double *pt2) {
double tmp;
tmp = *pt1;
*pt1 = *pt2;
*pt2 = tmp;

15

}
void scambiacx(complex *pt1, complex *pt2) {
complex tmp;
tmp = *pt1;
*pt1 = *pt2;
*pt2 = tmp;
}

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 uno: %i due: %i ", unoi, duei);
scambiain(&unoi,&duei);
printf("\n uno: %i due: %i ", unoi, duei);
printf("\n a: %c b: %c ", ac, bc);
scambiach(&ac,&bc);
printf("\n a: %c b: %c ", ac, bc);
printf("\n fla: %f flb: %f ", fla, flb);
scambiafl(&fla,&flb);
printf("\n fla: %f flb: %f ", fla, flb);
printf("\n dba: %lf dbb: %lf ", dba, dbb);
scambiadb(&dba,&dbb);
printf("\n dba: %lf dbb: %lf ", dba, dbb);
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);
}

Output della esecuzione di scambia:


uno:
uno:
a: a
a: b
fla:
fla:
dba:
dba:
cx1:
cx1:

1 due: 2
2 due: 1
b: b
b: a
1.010000 flb: 2.020000
2.020000 flb: 1.010000
10.003000 dbb: 23.995000
23.995000 dbb: 10.003000
1.200000 2.100000 cx2: 91.200000 92.100000
91.200000 92.100000 cx2: 1.200000 2.100000

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>
/* definizione del tipo numero complesso */
typedef struct {
double realp, imgp;
} complex;
/* funzione di scambio fra due variabili */
void scambia(char tipo, void *pt1, void *pt2) {
char tmpch;
int tmpin;
double tmpfl;
complex tmpcx;
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 a: %c b: %c ", ac, bc);
pt1 = &ac; pt2 = &bc;
scambia('c',pt1,pt2);
printf("\n a: %c b: %c ", ac, bc);
printf("\n f1: %lf f2: %lf ", unof, duef);
pt1 = &unof; pt2 = &duef;
scambia('f',pt1,pt2);
printf("\n f1: %lf f2: %lf ", unof, duef);
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);
}

Output della esecuzione di scambia:


uno: 111 due:
uno: 222 due:
a: Y b: Z
a: Z b: Y
f1: 10.100020
f1: 20.200010
cx1: 1.200000
cx1: 8.200000

222
111
f2: 20.200010
f2: 10.100020
9.300000 cx2: 8.200000 8.300000
8.300000 cx2: 1.200000 9.300000

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.
Massimo Comun Divisore
Il Massimo Comun Divisore fra due numeri si pu ottenere, secondo il geniale algoritmo di Euclide,
osservando che il MCD(n,m) = MCD(n-m,m), dove n > m o ancora MCD(n,m) = MCD(m, n%m)
dove n%m il resto della divisione n/m.
file: MCDiv.c
/*************************************************************
** Massimo Comun Divisore fra due numeri interi
**

18

** algoritmo di Euclide:
**
** 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
scanf("%d %d", &x,
printf("Il Massimo
printf("Il Massimo
}

due numeri interi\n");


&y);
Comun Divisore fra %d e %d e' %d\n", x, y, MCDric(x,y));
Comun Divisore fra %d e %d e' %d\n", x, y, MCDite(x,y));

altra forma della funzione MCDite:


int MCDite(int a, int b)
{
while ((a != 0) && (b != 0)) {
if (a > b)
a = a % b;
else
b = b % a;
}
return ((a>b) ? a : b);
}

altra forma della funzione MCDric:

int MCDric(int a, int b)


{
return (b ? MCDric(b, a % b) : a);

19

Media ricorsiva
Anche la media pu essere ottenuta ricorsivamente osservando che:

X i X i 1

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]);
}
media = Media(elementi, num_elem);
printf ("\nLa media e`: %f", media);
}

Polinomio
Per un polinomio di grado n nella variabile x di coefficienti ci, vale la seguente eguaglianza:
Pn ( x) cn x n cn 1 x n1 c1 x1 c0 c0 x (c1 x (c2 x cn ))

codificata nella funzione ricorsiva Poli, come appare qui di seguito.


file: poli.c
/*

calcolo ricorsivo di un polinomio

*/

#include <stdio.h>

20

");

#define MAX_SZ 10
/* i parametri passati sono: il valore della variabile x, il vettore
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;
printf("\nInserisci grado del polinomio\n");
cont = scanf("%d", &n);
/* lettura grado = lunghezza */
while(cont == 1){
printf("\nInserisci i %i coefficienti\n", n + 1);
for(i = 0; i < n + 1; i++)
scanf(" %f", &coeff[i]);
printf("\nInserisci il valore x:
scanf(" %f",&x);

");

polinomio = Poli(x,coeff,n);
printf("\n valore: %f",polinomio);
printf("\nInserisci grado del vettore\n");
cont = scanf("%d", &n);
/* lettura grado = lunghezza */
}

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;

21

if (x < v[k])
j = k - 1;
else
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);
}

Operazioni diverse su strighe di caratteri


La funzione inverti inverte lordine dei caratteri in una stringa e ritorna la sua lunghezza,
esistono innumerevoli modi per ottenere questo risultato, la funzione usa la tecnica di copiare la
stringa in unarea temporanea e poi ricopiare un carattere alla volta da questa nella posizione finale.
Si noti luso delloperatore prefisso -i nella funzione.
file: strinv.c

#include<stdio.h>
#define MAX_LN 30
int main() {
char parola[MAX_LN];
int lung;
int inverti(char parola[]);
printf("\ninserisci la parola: ");
scanf("%s", parola);
lung = inverti(parola);
printf("\n %s lung: %d", parola, lung);
return 0;
}
int inverti(char parola[]) {
char alorap[MAX_LN];
int l, i = 0;
while(parola[i] != '\0' && i < MAX_LN)
{ alorap[i] = parola[i]; i++; }
l = --i;
while(i >= 0)
{ parola[l - i] = alorap[i]; i--; }
return (l + 1);
}

22

La funzione ConsumaDoppi riceve in input il puntatore a una stringa di caratteri ed elimina i


caratteri contigui ripetuti. Nella prima versione viene usata unarea temporanea in cui si costruisce
il risultato finale, ricopiato poi nella stringa di partenza. Pi oltre viene riportata una versione pi
compatta dellalgoritmo (codificata in due modi equivalenti) in cui non vengono usate variabili
intermedie ma la stringa ricopiata su se stessa, un carattere alla volta, utilizzando una coppia di
indici diversi.
file:string0.c
#include<stdio.h>
#include<stdlib.h>
void ConsumaDoppi(char *Str) {
int i, j = 0;
char Strtmp[30];
for(i = 0; Str[j] != '\0'; i++) {
Strtmp[i] = Str[j];
j++;
while(Strtmp[i] == Str[j])
j++;
}
Strtmp[i] = '\0';
for(i = 0; Strtmp[i] != '\0'; i++)
Str[i] = Strtmp[i];
Str[i] = '\0';
}
void main() {
char stringa[30];
scanf("%s", stringa);
printf("\n%s", stringa);
ConsumaDoppi(stringa);
printf("\n%s ", stringa);
}

versione 2 (sola funzione)

void ConsumaDoppi(char *Str) {


int i, j = 0;
for(i = 0; Str[i] != '\0'; i++) {
Str[i] = Str[j++];
while(Str[i] == Str[j])
j++;
}
}

versione 2b (sola funzione variazione)


void ConsumaDoppi(char *Str) {
char *punt = Str;
for(; *Str != '\0';) {
*Str = *punt++;
while(*Str == *punt)
punt++;
Str++;
}
}

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);
}

Output della esecuzione di:


conta1 < conta1.c (il programma legge la codifica contenuta nel file conta1.c)
numeri
0
1
2
3
4
5
16
4
4
4
0
0
lettere
A
B
C
D
E
F
U
V
W
X
Y
Z
30
6 19 13 21 26
4
1
1
0
0
6
spazi = 193, altri = 313

6
4

7
0

8
0

9
1

70

17

34

11

13

25

10

30

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;
/* stampa lista (iterativa) in ordine diretto */
void StampaLista(Lista l) {
while (l != NULL) {
printf("%i\n",l->Info);
l = l->next;
}
}
/* stampa lista (ricorsiva) in ordine diretto */
void StampaDiretta(Lista l) {
if(l) {
printf("%i\n", l->Info);
StampaDiretta(l->next);
}
}
/* stampa lista (ricorsiva) in ordine inverso */
void StampaInversa(Lista l) {

25

if(l) {
StampaInversa(l->next);
printf("%i\n",l->Info);
}
}
/* conteggio (iterativo) del numero di elementi di una lista */
int LungListaI(Lista l){
int lung = 0;
while(l) {
lung++;
l = l->next;
}
return (lung);
}
/* conteggio (ricorsivo) del numero di elementi di una lista */
int LungListaR(Lista l) {
int lung = 0;
if (l != NULL) {
lung = 1 + LungListaR(l->next);
}
return (lung);
}
/* inserimento in testa */
void InserisciInTesta(Lista *lp, ElmLista *elp) {
elp->next = *lp;
*lp = elp;
}
/* inserimento in coda (iterativo) */
void InserisciInCoda(Lista *lp, ElmLista *elp) {
ElmLista *elementoCorrente;
elementoCorrente = *lp;
if (*lp == NULL)
*lp = elp; /* caso di lista vuota */
else {
while (elementoCorrente->next != NULL) {
elementoCorrente = elementoCorrente->next;
}
elementoCorrente->next = elp;
}
elp->next = NULL;
}
/* inserimento in coda (ricorsivo) */
void InserisciInCodaR(Lista *lp, ElmLista *elp) {
if (*lp == NULL) {
*lp = elp;
/* fine della lista */
elp->next = NULL;
}
else {
InserisciInCodaR(&(*lp)->next, elp);
}
}
/* inserimento in ordine (si suppone la lista in ordine crescente) */

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;
}
/* il primo elemento (se esiste) viene tolto e ritornato il puntatore */
ElmLista *TogliDaTesta(Lista *lp) {
ElmLista *elementoCorrente;
elementoCorrente = *lp;
if (elementoCorrente != NULL)
*lp = elementoCorrente->next;
return elementoCorrente;
}

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
printf("\n
printf("\n
printf("\n

%i
%i
%i
%i

",LungListaI(Lista1));
",LungListaR(Lista1));
",LungListaI(NULL));
",LungListaR(NULL));

StampaDiretta(Lista1);
printf("\n");
StampaInversa(Lista1);
printf("\n");
*/
}

Output della esecuzione di listai:


1
2
3
5
0
1
2
3
4
5
6
6
5
4
3
2
1
0

Pile e code (strutturate come lista)


I programmi che seguono, pila e coda (entrambi composti da file .h e .c) non sono altro
che la versione del problema affrontato in precedenza mediante vettori nellesercizio pilacoda,
ora risolto su strutture dinamiche. Gli elementi sono, al solito, interi. Le funzioni di inserimento e
eliminazione si incaricano di richiedere e liberare la memoria. Invece di utilizzare la tecnica di
passare alle funzioni il doppio puntatore, viene passato quello semplice ma la funzione ritorna il
nuovo puntatore al programma chiamante.
Si noti come la struttura della lista della coda sia ciclica, lultimo elemento inserito contiene
lindirizzo del primo (che sar anche il primo da togliere). Con un solo puntatore si raggiungono
28

perci entrambi gli elementi interessati alla operazione e il numero di istruzioni eseguite
indipendente dalla lunghezza della coda, analogamente al caso della pila. In questo modo per
bisogna distinguere fra loro i tre casi: in cui non ci siano elementi accodati, ce ne sia uno solo e ce
ne siano due o pi.
file: pila.h
typedef struct _elPila
{
int
Info;
struct _elPila *prossimo;
}
elPila, *Pila;
Pila
{

Inserisci(Pila p, int el)


elPila

*nuovoElemento;

/* richiedo memoria per nuovo elemento*/


nuovoElemento = (elPila *) malloc(sizeof(elPila));
if (nuovoElemento == NULL) return (NULL);
/* copio contenuto informativo nel nuovo elemento */

nuovoElemento->Info = el;
nuovoElemento->prossimo = p;
p = nuovoElemento;
return(p);

Pila
{

Togli(Pila p, int *el)

Pila punt;
if (p == NULL) return (NULL);
*el = p->Info;
punt = p->prossimo;
free(p);
return (punt);

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;
Coda
{

Inserisci(Coda pc, int el)


elCoda

*nuovoElemento;

/* richiedo memoria per nuovo elemento*/


nuovoElemento = (elCoda *) malloc(sizeof(elCoda));
if (nuovoElemento == NULL) return (NULL);
/* copio contenuto informativo nel nuovo elemento */
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

}
Coda
{

Togli(Coda pc, int *el)


Coda punt;
if (pc == NULL) return (NULL);
punt = pc->prossimo;
if (pc->prossimo == pc) {
printf("%i ", pc->Info);
free(punt);
return (NULL);
}
*el = punt->Info;
pc->prossimo = punt->prossimo;
free(punt);
return (pc);

}
void
{

StampaCoda(Coda pc)
Coda punt;
if (pc == NULL) {
printf("la pila non ha elementi\n");
return;
}
punt = pc;
do
{
punt = punt->prossimo;
printf("%i\n", punt->Info);
}
while (punt != pc);

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;
char *strdupz(char *);
Treeptr addtree(Treeptr, char *);
void treeprint(Treeptr);
Treeptr talloc(void);
void StampaAlbero(Treeptr, int);
/* costruisce albero dinamico inserendo parole */
int main(void)
{
Treenode *root;
char word[MAXWORD];
char *fine = "END";

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;

/* aggiunge un nodo con w, a livello di *p o sotto */


Treenode *addtree(Treenode *p, char *w)
{
int cond;
if (p == NULL )
/* nuova parola */
{
p = talloc();
/* crea un nuovo nodo */
p->word = strdupz(w);
p->count = 1;
p->left = p->right = NULL;
}
else if ((cond = strcmp(w, p->word)) == 0)
p->count++;
/* parola ripetuta */
else if (cond < 0)
/* minore: sottoalbero sinistro */
p->left = addtree(p->left, w);
else
/* maggiore: sottoalbero destro */
p->right = addtree(p->right, w);
return p;
}
/* visita e stampa dell'albero p in ordine simmetrico */
void treeprint(struct tnode *p)
{
if (p != NULL)
{

33

treeprint (p->left);
printf("%s (%d)\n", p->word, p->count);
treeprint(p->right);

}
/* stampa la forma e il contenuto dell'albero */
void StampaAlbero(Treenode *p, int l)
{
int i;
if (p != NULL)
{
StampaAlbero(p->right, l + 1);
for (i = 0; i < l; i++)
printf(" ");
printf("%s\n",p->word);
StampaAlbero(p->left, l + 1);
}
}
/* crea un tnode */
Treenode *talloc(void)
{
return (Treeptr)malloc(sizeof(Treenode));
}
/* crea una copia della stringa letta s */
char *strdupz(char *s)
{
char *p;
p = (char *)malloc(strlen(s) + 1);
if (p != NULL)
strcpy(p, s);
return p;
}

Esempio di output della esecuzione di albero2:


inserisci una parola alla volta <END termina la sequenza>
per correr migliori acque alza le vele
ormai la navicella del mio ingegno
che lascia dietro a se' mar si' crudele
END
ALBERO ORDINATO
a (1)
acque (1)
alza (1)
che (1)
correr (1)
crudele (1)
del (1)
dietro (1)
ingegno (1)
la (1)
lascia (1)
le (1)
mar (1)
migliori (1)
mio (1)
navicella (1)

34

ormai (1)
per (1)
se' (1)
si' (1)
vele (1)
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
Esempio di scrittura, lettura e aggiornamento di un file
I record contenuti nel file prova.txt hanno lunghezza fissa pari a 20 byte, se si prova perci a
visualizzare con un editor il file costruito, in coda ad ogni parola appaiono una serie di caratteri
sporchi non stampabili fino al completamento dei 20 byte. Il problema non si pone se si stampano le
stringhe con un formato, per esempio con listruzione printf, in questo caso infatti il carattere
\0 segnala la fine della stringa.
Il programma operio vuole illustrare le istruzioni di I/O, in particolare: fopen, fclose,
fwrite, fread, ftell, fseek. Le ultime due servono rispettivamente a memorizzare e a
posizionare lindirizzo di lettura/scrittura nel file.
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;
if ((fp = fopen(filename, "w")) == NULL) exit (-1);
printf("inserisci parola\n");
scanf(" %s", parola);
while (parola[0] != '#') {
fwrite (parola, sizeof(parola),1,fp);
printf("inserisci parola\n");
scanf("%s", parola);
}
fclose(fp);
if ((fp = fopen(filename, "r+")) == NULL) exit (-1);
printf("\ninserisci la parola da sostituire e la parola nuova\n");
scanf("%s %s", parolaDaTrovare, parolaNuova);
for (;;) {
n = fread(parola, sizeof(parola),1,fp);
if (n == 0) {
printf("parola non trovata\n");
exit(-1);
}
if (strcmp(parola,parolaDaTrovare) == 0)
break;
}
pos = ftell(fp);
pos = pos - sizeof(parola);
fseek(fp,pos,0);
fwrite(parolaNuova,sizeof(parola),1,fp);
fclose(fp);
}

36

You might also like