Sei sulla pagina 1di 84

-1-

*----------------------------------------------------*

PROGETTO DI PROGRAMMAZIONE II
E LABORATORIO DI
PROGRAMMAZIONE II

LIPARDI PIERLUIGI
0124000867
Esercizi svolti: Sistema operativo: Windows 8.1 a 64 bit
Livello 1 : 36/36 Compilatore: Dev-C++ versione 5.6.2
Livello 2 : 2/19 Pacchetto utilizzato: Microsoft Office 2007
Livello 3 : 1/20 Altri programmi : Paint e Adobe Reader

Gli esercizi sono stati commentati per permettere al lettore una migliore
comprensione sia dei passaggi svolti sia della logica che c’è dietro ogni
programma nella relazione. C’è anche un sommario per arrivare facilmente
agli esercizi desiderati.

*----------------------------------------------------*
-2-

Indice:
ESERCIZI LIVELLO 1
Argomento 1: Operatori sui singoli bit, bitwise operators
01……………………………………………………………………………………………………pag 4
02……………………………………………………………………………………………………pag 4-5
03……………………………………………………………………………………………………pag 5-7
04……………………………………………………………………………………………………pag 8-10

Argomento 2: Sistemi aritmetici di un computer, tipo numerico intero


05……………………………………………………………………………………………………pag 10-11
06……………………………………………………………………………………………………pag 11-12
08……………………………………………………………………………………………………pag 13
09……………………………………………………………………………………………………pag 14
10……………………………………………………………………………………………………pag 15-16
11……………………………………………………………………………………………………pag 17

Argomento 3: Sistemi aritmetici di un computer, tipo numerico reale floating-point


12……………………………………………………………………………………………………pag 18-19
14……………………………………………………………………………………………………pag 19-22
18……………………………………………………………………………………………………pag 23-24
19……………………………………………………………………………………………………pag 24-25
20……………………………………………………………………………………………………pag 25-26

Argomento 4: Le stringhe
22(Quiz)……………………………………………………………………………………………..pag 27
23…………………………………………………………………………………………………….pag 27-28
24…………………………………………………………………………………………………….pag 29-30
25…………………………………………………………………………………………………….pag 31
26_parte1…………………………………………………………………………………………….pag 32

Argomento 5: Allocazione dinamica della memoria


29……………………………………………………………………………………………………..pag 33-34
30……………………………………………………………………………………………………..pag 35-38

Argomento 7: Gestione dei file


33……………………………………………………………………………………………………..pag 38-39

Argomento 8: Strutture dati dinamiche lineari: pila, coda e lista lineare


36……………………………………………………………………………………………………..pag 40-41
37……………………………………………………………………………………………………..pag 42-43
39……………………………………………………………………………………………………..pag 44-45
41……………………………………………………………………………………………………..pag 46-49
44……………………………………………………………………………………………………..pag 50-60

Argomento 9: Strutture dati dinamiche gerarchiche alberi, alberi binari, alberi di ricerca, heap
49……………………………………………………………………………………………………..pag 61-62

Argomento 10: Strutture dati dinamiche reticolari, grafi


55……………………………………………………………………………………………………..pag 63-65
56……………………………………………………………………………………………………..pag 66-68

Argomento 11: Tecniche di programmazione ricorsiva


61……………………………………………………………………………………………………...pag 69-70
62……………………………………………………………………………………………………...pag 70-71
-3-

Argomento 12: Algoritmi di ordinamento Divide et Impera: Quick-Sort, Merge-Sort, Heap-Sort


67……………………………………………………………………………………………………pag 72-75
69……………………………………………………………………………………………………pag 75-77
70……………………………………………………………………………………………………pag 78

ESERCIZI LIVELLO 2
Argomento 3: Sistemi aritmetici di un computer, tipo numerico reale floating-point
17……………………………………………………………………………………………………pag 79-80

Argomento 12: Algoritmi di ordinamento Divide et Impera: Quick-Sort, Merge-Sort, Heap-Sort


71……………………………………………………………………………………………………pag 81-82

ESERCIZI LIVELLO 3
Argomento 12: Algoritmi di ordinamento Divide et Impera: Quick-Sort, Merge-Sort, Heap-Sort
73……………………………………………………………………………………………………pag 83-84
-4-

/*ESERCIZIO 1: Scrivere una function C char low_upp(char ch) che cambia il carattere
in input da minuscolo a maiuscolo e viceversa automaticamente */

#include <stdio.h>
char low_upp(char ch);
void main()
{
char ch;
printf("Puoi scegliere di inserire il carattere o minuscolo o maiuscolo.\n");
printf("\nInserisci qui il carattere: ");
scanf("%c",&ch);
ch=low_upp(ch);
printf("\nIl carattere convertito e' questo: %c",ch);
}

/*La function prende in input un carattere e restituisce un dato dello stesso tipo.
La relazione tra un carattere maiuscolo e uno minuscolo riguarda solamente un bit: il
sesto bit contando da destra verso sinistra la stringa di 8 bit relativa al carattere. Si puo' agire
sul sesto bit della stringa mediante l'operatore bitwise XOR(^) che si comporta in questo modo: se i
due bit operandi sono uguali il risultato e’ 0, invece se i due bit operandi sono diversi il risultato
e' 1.
Ad esempio:
A= 65 ossia 01#0#0 0001 in binario
a= 97 ossia 01#1#0 0001 in binario*/
char low_upp(char ch)
{
char carattere;
carattere=ch^32;
return carattere;
}

Screenshot esercizio 1

/*ESERCIZIO 2: Scrivere una function C char rotate(char ch, char n_bit) per ruotare di n bit (n_bit),
verso sinistra o verso destra (rispettivamente per n_bit<0 e per n_bit>0), il contenuto di una
variabile char mediante gli operatori bitwise*/

#include<stdio.h>
char rotate(char ch, char n_bit);
void main()
{
unsigned char ch;/*unsigned perche' se no dopo la rotazione esce un valore con il segno*/
int n_bit;/*il numero di bit da shiftare*/
printf("Inserisci qui il carattere (a...z oppure A...Z): ");
scanf("%c",&ch);
printf("Il valore intero del carattere nel codice ASCII vale: %d",ch);
printf("\n\nDi quanti bit vuoi fare lo shift?");
printf("\nMetti un numero negativo per shiftare a sinistra");
printf("\nMetti un numero positivo per shiftare a destra");
printf("\nNumero di bit inserito: ");
scanf("%d",&n_bit);
ch=rotate(ch,n_bit);
printf("\nEcco il valore dopo lo shift: %d",ch);

}
-5-

/*Lo scopo della funzione e’ ruotare i bit di un char dato di n bit.


In input abbiamo ch = char da ruotare, n_bit = il numero di bit da shiftare.
In uscita avremo i bit del char ruotati.
Es. 0110 0110(102 in decimale e f nel codice ASCII) e n_bit = 2 (shift a destra)
Allora ch = 0001 1001 (shiftiamo a destra) e tmp = 0000 0010 che shifto a sinistra di 8-n_bit,
avendo 1000 0000(ultimi 2 bit saranno i primi). Alla fine avro’, return 1001 1001*/
char rotate(char ch, char n_bit)
{
char temp;/*variabile temporanea serve per salvare i bit che andrebbero persi nello shift*/
temp=ch;/*eguaglio il valore della variabile ch con temp che non ha inizialmente alcun valore*/

if(n_bit>0)/*rotazione a destra*/
{
ch=ch>>n_bit;/*questo e' lo shift principale*/
temp=temp<<8-n_bit;/*eseguo il salvataggio dei bit che verrebbero persi nello shift*/
return ch|temp;/*uniamo le due porzioni attraverso l'operatore bitwise or(inclusivo)*/
}
else if(n_bit<0)/*rotazione a sinistra*/
{
n_bit=-n_bit;/*cambiamo il segno di n_bit in positivo perche' a noi serve il modulo*/
ch=ch<<n_bit;/*questo e' lo shift principale*/
temp=temp>>(8-n_bit);/*eseguo il salvataggio dei bit che verrebbero persi nello shift*/
return ch|temp;/*UNIAMO le due porzioni attraverso l'operatore bitwise or(inclusivo)*/
}
}

Screenshot esercizio 2

/*ESERCIZIO 3
Scrivere una function C che, dopo aver estratto i bit da una variabile intera X (tipo char, short o
int) ne calcola il valore corrispondente dalla formula:
Val_X = b[n-1] * 2^(n-1) + ... + b[2] * 2^2 + b[1] * 2^1 + b[0] * 2^0
dove b e' l'array dei bit di X:b[j], per j=0,1....,n-1 dal meno significativo al piu' significativo,
(dove n=8 per il tipo char, n=16 per il tipo short o n=32 per il tipo int).
Confrontare il risultato con il valore immesso per la variabile intera X dichiarata una volta signed ed
un'altra unsigned.*/

#include<stdio.h>
#include<math.h>/* mi serve per usare la potenza pow(x,y)*/
#include<stdlib.h> /*questa libreria mi serve per usare exit()*/
#define MAX_LEN 32 /*questo e' il numero massimo di bit di intero long*/
void bit_show(short len, char ch[], short bit[]);
void visualizza_dec(short len, short bit[]);
void main()
{
short menu, bit[MAX_LEN], k;
unsigned char len;/*len mi rappresenta la lunghezza della variabile in esame*/

/* COSTRUTTO UNION
Occorre una function che visualizzi la rappresentazione binaria di un numero di tipo qualunque.
Per realizzare una function del genere occorre usare il costrutto UNION in cui
le variabili possono essere considerate viste diverse di una stessa area di memoria.
La dichiarazione risulta simile a quella delle struct in C.
La differenza sostanziale tra i due costrutti riguarda come sono allocati i byte della memoria
per ciascun tipo di dato. In particolare:
- nella struct ogni campo ha un suo indirizzo
- nella union tutti i campi partono tutti dallo stesso indirizzo(si sovrappongono)
-6-

D'altra parte, visto che la UNION usa lo stesso numero di byte per le varie variabili, avremo
che:
- la variabile L (array di 1 long) utilizzera' 32 bit (4 byte)
- la variabile S (array di 2 short) utilizzera' 2x16 bit = 32 bit
- la variabile C (array di 4 char) utilizzera' 4x8bit = 32 bit
Le 3 variabili condividono la stessa area di memoria e, quindi, fanno riferimento alle stesse
locazioni di memoria */
union word32bit
{
long L[1];
short S[2];
char C[4];
}word;
puts("\n\nFa la tua scelta");
puts("\n[0] Esci");
puts("\n[1] Intero char in binario");
puts("\n[2] Intero short in binario");
puts("\n[3] Intero long in binario\n");
do{
printf("\n\nScelta: ");
fflush(stdin);
scanf("%hd", &menu);
switch(menu)
{
case 0 : exit(0);
case 1 : /*visualizzo i bit corrispondenti a una variabile di tipo char*/
len=sizeof(char);
printf("Immettere un intero char -> ");
fflush(stdin);
scanf("%d", &(word.C[0]));/*fa rifermento alla prima componente*/
puts("Char in decimale e esadecimale");
printf("C=%+10hd, hex=%02x",word.C[0],word.C[0]);
bit_show(sizeof(char),word.C,bit);/*estraggo i bit*/
visualizza_dec_bin(sizeof(char), bit);
break;
case 2 : /*visualizzo i bit corrispondenti a una variabile di tipo short*/
len=sizeof(short);
printf("Immettere un intero short -> ");
fflush(stdin);
scanf("%hd", &(word.S[0]));/*fa riferimento alla prima componente*/
puts("Short in decimale e esadecimale");
printf("S=%+10hd, hex = %04x; ", word.S[0],word.S[0]);
bit_show(sizeof(short),word.S, bit);/*estraggo i bit*/
visualizza_dec_bin(sizeof(short), bit);
break;
case 3 : /*visualizzo i bit corrispondenti a una variabile di tipo long int */
len=sizeof(long);
printf("Immettere un intero long -> ");
fflush(stdin);
scanf("%ld", &(word.L[0]));/*fa riferimento alla prima componente*/
puts("Long in decimale e esadecimale");
printf("L=%+10d, hex =%08lx; ", word.L[0],word.L[0]);
bit_show(sizeof(long),word.L,bit);/*estraggo i bit*/
visualizza_dec_bin(sizeof(long), bit);
break;
default : exit(1);
}
for(k = 8*len-1; k>=0; k--)/*stampo i bit*/
{
(k%4 == 0) ? printf("%1d ",bit[k]) : printf("%1d", bit[k]);
}

}while(menu!=0);
}
-7-

void visualizza_dec_bin(short len, short bit[])


{
short i;
long c=0;
for(i=8*len-1;i>=0;i--)
{
c = c+(bit[i]*(pow(2,i))); /*Calcolo del valore intero partendo dalla stringa
binaria della variabile di input, mediante la formula data. Viene eseguito un
ciclo per 8 volte effettuando la somma tra le potenze di 2 successive.
Cioe’ ad esempio se voglio convertire 0000 0010 in base dieci devo fare:
(0*2^7)+(0*2^6)+(0*2^5)+(0*2^4)+(0*2^3)+(0*2^2)+(1*2^1)+(0*2^0) */
}
printf("\n\nX in decimale = %d\nX in binario = ",c);
}

/*La funzione bit_show ci permette di estrarre i bit di variabili intere


char, short e long e di variabili float. E' possibile visualizzare il contenuto di
variabili massimo a 32 bit.
Alla funzione gli passiamo len cioe' la lunghezza come byte della variabile che ci interessa
l'array di di tipo char[], l'array bit che restituisce la rappresentazione binaria.
I bit vengono estratti a blocchi di byte*/
void bit_show(short len, char ch[], short bit[])
{
short j,jc;
char c;
/*azzera tutte le componenti dell'array bit*/
for(j=0; j<MAX_LEN; j++)
{
bit[j]=0;
}
/*Questo ciclo avanza sul numero di byte e, nel caso del char viene eseguito 1 volta,
nel caso dello short viene eseguito 2 volte*/
for(jc=0; jc<len; jc++)
{
/*memorizza l'informazione individuata dall'i-esimo byte*/
c=ch[jc];/*Ricava cio' che trovi al byte i*/
/*scorri i bit del byte preso in esame*/
for(j=0;j<8;j++)
{
bit[j+8*jc] =c&1;/*individuando byte e bit, inserisce il valore del primo bit.
Ci sono due indirizzamenti:
uno sul bit del byte attuale che e' la variabile j
l'altro jc indica il byte corrispondente e quindi
moltiplicato per 8*/
c=c>>1;/*sposta di un posto per controllare il bit successivo*/
}
}
}
Screenshot esercizio 3
-8-

/*ESERCIZIO 4
Scrivere una function C per estrarre dalla variabile intera X i k bit più significativi o meno
significativi,
dove X e k sono i parametri di input, usando:
1) Una maschera.
2) L’operatore di shift (>> o <<).
3) Il prodotto o la divisione per potenze di 2 */

#include <stdio.h>
#include<math.h>
#define MAX_LEN 8 /*questa e' la lunghezza massima della stringa in bit*/
/*la scelta di unsigned e' perche (2^8-1) mi darebbe un valore negativo,
quindi consideriamo il valore x in modulo affinche' cio' non accadi*/
unsigned char shift(unsigned char x,short k,short bit[]);
unsigned char maschera(unsigned char x,short k);
unsigned char prod_div(unsigned char x,short k,short bit[]);
void bit_show(short len,unsigned char x,short bit[]);
void stampa_bit(short bit[]);
void main()
{
unsigned char ris_shift, ris_prodiv;
unsigned char mask;
short bit[MAX_LEN];
short len;
unsigned char x;
short k;
printf("Inserisci il valore della variabile X di tipo intero: ");
scanf("%d",&x);
printf("\nIl valore iniziale della variabile e': ");
bit_show(sizeof(short),x,bit);
stampa_bit(bit);
printf("\n");
/*adesso decidiamo se estrarre i bit piu' significativi o meno significativi,
inserendo un numero in input*/
printf("\nQuanti bit vuoi estrarre?\nInserisci un valore negativo per i bit meno
significativi\noppure un valore positivo per i bit piu' significativi\n");
printf("\nEcco il numero di bit che hai scelto: ");
scanf("%hd",&k);
fflush(stdin);
mask=maschera(x,k);/*ecco la chiamata alla funzione relativa alla maschera*/
printf("\nBIT ESTRATTI CON LA MASCHERA: ");
bit_show(sizeof(short),mask,bit);
stampa_bit(bit);
ris_shift=shift(x,k,bit);/*ecco la chiamata alla funzione relativa allo shift*/
printf("\nBIT ESTRATTI CON LO SHIFT: ");
bit_show(sizeof(short),ris_shift,bit);
stampa_bit(bit);
ris_prodiv =prod_div(x,k,bit);/*ecco la chiamata alla funzione relativa al prodotto-divisione*/
printf("\nBIT ESTRATTI CON IL PRODOTTO: ");
bit_show(sizeof(short),ris_prodiv,bit);
stampa_bit(bit);
}

void bit_show(short len,unsigned char x,short bit[])


{
short i,j;
for(i=0; i<MAX_LEN; i++)/*azzera tutte le componenti dell'array bit*/
{
bit[i]=0;
}
/*Questo ciclo avanza sul numero di byte e avendo un char(1 byte), il ciclo avviene 1 volta*/
for(i=0; i<len; i++)
for(j=0;j<8;j++)
{
bit[j+8*i] = x&1;/*individuando byte e bit, inserisce il valore del primo bit*/
/*Ci sono due indirizzamenti:
uno sul bit del byte attuale che e' la variabile j
l'altro i indica il byte corrispondente e quindi moltiplicato per 8*/
x=x>>1;/*sposta di un posto per controllare il bit successivo*/
}
}
-9-

unsigned char maschera(unsigned char x,short k)


{
short i;
unsigned char mask=0;
if(k>0)
{
/*questo ciclo for ci consente di mascherare i bit che vogliamo estrarre coprendoli con
tutti 1*/
for(i=1;i<=k;i++)
{
mask=mask<<1|1;
}
mask=mask<<(sizeof(unsigned char)*MAX_LEN-k);/*spostiamo dall'estrema destra
all'estrema sinistra di 8-k bit il valore di mask*/
}
else if(k<0)
{
/*nel caso dell'estrazione dei bit meno significativi non c'e' bisogno di fare nulla*/
k=-k;//mi interessa il modulo ma non il segno
/*questo ciclo for ci consente di mascherare i bit che vogliamo estrarre coprendoli con
tutti 1*/
for(i=1;i<=k;i++)
{
mask=mask<<1|1;
}
}
return x&mask;/*diamo come valore di ritorno l'intersezione tra il valore x
precedentemente inserito e il valore di mask attraverso l'operatore AND*/
}

unsigned char shift(unsigned char x,short k,short bit[])


{
if(k>0)
{
x =x>>(MAX_LEN - k);/*shifto a destra per eliminare i bit che non mi servono*/
x =x<<(MAX_LEN - k);/*shifto a sinistra per riportare alla posizione iniziale i
bit piu' significativi che desidero estrarre*/

}
else if(k<0)
{
k=-k;/*cambiamo segno a k perche' ci serve il modulo*/
x =x<<(MAX_LEN - k);/*shifto a sinistra per eliminare i bit che non mi servono*/
x =x>>(MAX_LEN - k);/*shifto a destra per riportare alla posizione iniziale i bit
meno significativi che voglio estrarre*/
}
return x;
}

unsigned char prod_div(unsigned char x,short k,short bit[])


/*nel caso della moltiplicazione e della divisione ricordiamo che:
-lo shift a destra >> mi serve per dividere
-lo shift a sinistra << mi serve per moltiplicare
Poi usiamo pow per indicare quanto deve valere il valore per il quale la variabile x deve essere
o moltiplicata o sottratta*/
{
if(k>0)
{
x=x/pow(2,MAX_LEN-k); /*shifta a destra = divisione per una potenza di 2*/
x=x*pow(2,MAX_LEN-k); /*shifta a sinistra = prodotto per una potenza di 2*/
}
else if(k<0)
{
k=-k;
x=x*pow(2,MAX_LEN-k); /*shifta a sinistra = prodotto per una potenza di 2*/
x=x/pow(2,MAX_LEN-k); /*shifta a destra = divisione per una potenza di 2*/
}
return x;
}
- 10 -

void stampa_bit(short bit[])


{
short i;
/*la stampa dei bit va dal piu' significativo al meno significativo:
cioe' se io ho il valore 2 che in binario vale 0000 0010, i bit 0000
sono i piu' significativi e i bit 0010 sono i meno significativi*/
for(i=MAX_LEN-1; i>=0; i--)
{
(i%4 == 0) ? printf("%1d ",bit[i]) : printf("%1d", bit[i]);
}

}
Screenshot esercizio 4

/*ESERCIZIO 5
Scrivere due function C di conversione di un intero positivo (int)
da base 10 a base 2 mediante l’algoritmo delle divisioni successive realizzato rispettivamente:
• Usando gli operatori di quoziente e resto della divisione intera;
• Usando gli operatori bitwise */
/*Ricordiamo questa cosa: a = c : b
Nell'espressione sopra, a rappresenta il quoziente (quoto nel caso di divisione senza resto),
b il divisore (cioè la quantità che divide) e c il dividendo (cioè la quantità da dividere) */

#include<stdio.h>
#define MAX_LEN 16/*questo e' il numero massimo di bit di intero short*/
void conv_quozerest(unsigned short n,unsigned short bit[]);
void conv_bitwise(unsigned short n, unsigned short bit[]);
void stampa(short bit[]);
void main()
{
unsigned short n;
unsigned short bit[MAX_LEN];/*questa variabile ci serve per la visualizzazione binaria*/
printf("Inserisci un numero INTERO POSITIVO: ");
scanf("%hd",&n);
printf("\nConversione con operatori di quoziente e resto:\n");
conv_quozerest(n,bit);
stampa(bit);
printf("\n\nConversione con operatori bitwise:\n");
conv_bitwise(n,bit);
stampa(bit);
}

void conv_quozerest(unsigned short n,unsigned short bit[])


{
short i=0,j;
/* Il ciclo e' ripetitivo e si interrompe se il quoziente sara' uguale a zero*/
/*il quoziente (n) inizialmente coincide con il numero che vogliamo invertire*/
do{
bit[i++] = n%2;/*Mediante il resto della divisione, costruiamo la stringa binaria*/
n=n/2;/*passiamo al quoziente successivo fino a che non e' terminato il ciclo*/
}while(n>0);
/*invertiamo le componenti dell'array*/
for(j=i;j<MAX_LEN;j++)
bit[j] = 0;
}
- 11 -

void conv_bitwise(unsigned short n,unsigned short bit[])


{
short i=0,j;
/* Il ciclo si interrompe se il quoziente sara' maggiore di zero*/
do
{
bit[i++] = n%2;/*Mediante il resto della divisione, costruiamo la stringa binaria*/
n=n>>1;/*Equivale a n = n/2 perche’ lo shift a destra equivale a dividere per 2*/
}while(n>0);
/*usciti dal while dobbiamo invertire le componenti dell'array*/
for(j=i;j<MAX_LEN;j++)
bit[j] = 0;
}

void stampa(short bit[])/*questa funzione mi stampa i bit nell'ordine dal piu' significativo
al meno significativo nel senso che bit[0]=LSB-LEAST SIGNIFICANT BIT*/
{
short i;
for(i=MAX_LEN-1;i>=0;i--)
{
(i%4 == 0) ? printf("%1d ",bit[i]) : printf("%1d", bit[i]);
}
}
Screenshot esercizio 5

/*ESERCIZIO 6
Scrivere una function C di conversione di un intero positivo da base 2 a base 10,
mediante l’algoritmo delle divisioni successive, che generi un array di caratteri
contenenti le cifre decimali.*/

#include <stdio.h>
#include<math.h>
#define MAX_LEN 8
#define LEN 3/*la massima lunghezza dell'array di caratteri deve essere 3 perche' possono essere
inseriti fino a 256 valori, incluso lo 0*/
void conv_base10tobase2(unsigned short n,unsigned short bit[]);
unsigned short conv_base2tobase10(unsigned short bit[]);
void array(unsigned short n, unsigned char cifre_decimali[]);
void stampa_bit(short bit[]);
void main()
{
short i,cifre;
unsigned short n;
unsigned short bit[MAX_LEN];
unsigned char cifre_decimali[LEN];/*questo array di caratteri mi conterra' le cifre decimali*/
/*Dato che l'intervallo di rappresentazione del tipo char e' [-128,+127] i numeri che vanno
presi in considerazione sono 256 includendo lo zero*/

// su 8 bit si rappresentano ((2^8)-1) valori dalla formula (2^n)-1 dove n e’ il numero di bit
printf("Inserisci un numero INTERO POSITIVO (scegli da [0,255] estremi compresi): ");
scanf("%hd",&n);
printf("\nEcco il numero convertito da base 10 a base 2: ");
conv_base10tobase2(n,bit);//chiamata alla funzione per convertire da base 10 a base 2
stampa_bit(bit);
n=conv_base2tobase10(bit);//chiamata alla funzione per convertire da base 2 a 10
printf("\n\nEcco il numero riconvertito da base 2 a base 10: %d",n);
array(n,cifre_decimali);
}
- 12 -

unsigned short conv_base2tobase10(unsigned short bit[])


{
short i=0,j;
unsigned short b=0;//questa variabile mi contiene il valore convertito

/*conversione da base 2 a base 10*/


for(i=0;i<MAX_LEN;i++)
{
b=b+bit[i]*(pow(2,i));/*calcolo b in base 10 partendo dalla stringa binaria, effettuando
dalla 0-sima componente dell'array bit la somma tra le potenze di 2 successive*/
}
return b;
}

void conv_base10tobase2(unsigned short n,unsigned short bit[])


{
/*conversione da base 10 a base 2*/
short i=0,j;
/* Il ciclo e' ripetitivo e si interrompe se il quoziente sara' uguale a zero*/
/*il quoziente inizialmente coincide con il numero che vogliamo invertire*/
do{
bit[i++] = n%2;/*mediante il resto della divisione, costruiamo la stringa binaria*/
n=n/2;/*passiamo al quoziente successivo fino a che non e' terminato il ciclo*/
}while(n>0);
/*invertiamo le componenti dell'array*/
for(j=i;j<MAX_LEN;j++)
{
bit[j]=0;
}
}

void stampa_bit(short bit[])


{
short i;
/*consideriamo che la stampa dei bit va da sinistra verso destra: quindi se io
ho per esempio 2 che in binario vale 0000 0010, 0000 sono i bit piu' significativi e
0010 sono i bit meno significativi */
for(i=MAX_LEN-1; i>=0; i--)
{
(i%4 == 0) ? printf("%1d ",bit[i]) : printf("%1d", bit[i]);
}

void array(unsigned short n, unsigned char cifre_decimali[])


{
short i=0,j=0;
do{
cifre_decimali[i++]=n%10+'0';/*visto che abbiamo un array di caratteri gli passiamo il
resto della divisione dello short n con 10 sommando '0' in carattere cosi'
da inserire le cifre decimali in questo array di caratteri*/
n=n/10;/*passiamo al quoziente successivo*/
}while(n>0);
/*invertiamo le componenti dell'array*/
for(j=i;j<LEN;j++)
{
cifre_decimali[j]=0;
}

printf("\n\nArray di cifre decimali generato: ");


/*stampiamo l'array cifre_decimali*/
for (i=LEN-1; i>=0; i--)
printf("[%c] ",cifre_decimali[i]);
}
Screenshot esercizio 6
- 13 -

/*ESERCIZIO 8
Scrivere una function C per eseguire l'addizione aritmetica binaria di due numeri
naturali p e q (p,q•appartenenti ad N) mediante gli operatori bitwise
(come da algoritmo in P-like in esercizio 11) , traducendo l'algoritmo di seguito riportato:
{Algoritmo di addizione binaria mediante operatori sui bit}
procedure binary_add(op1,op2)
rip:=1;
while rip>0
sum:=bitXOR(op1,op2);
rip:=bitAND(op1,op2);
rip:=leftSHIFT(rip,1);
op1:=sum; op2:=rip;
endwhile */

#include<stdio.h>
short addizione(short p,short q);
void main()
{
short p,q;
printf("Inserisci il primo numero di tipo intero (p): ");
scanf("%hd",&p);
printf("\nInserisci il secondo numero di tipo intero (q): ");
scanf("%hd",&q);
/*E' ovvio che se il risultato sarà negativo, si e' verificato il fenomeno dell'overflow che
si presenta quando si tenta di scrivere un numero piu’ grande del massimo consentito dalla
configurazione di n_bit: nel nostro caso il range dello short e’ [0,+32767]*/
printf("\nL'addizione aritmetica vale: %hd",addizione(p,q));
}

short addizione(short p,short q)


{
short somma;
short riporto=1;
/*Il ciclo viene eseguito finchè non si ha un riporto maggiore di zero*/
while(riporto>0)
{
somma=p^q;/*qui facciamo la somma senza riporto*/
riporto=p&q;/*qui facciamo solamente il riporto*/
riporto=riporto<<1;/*qui portiamo il riporto a sinistra, facendolo shiftare di 1*/
p=somma;/*l'operando p conterrà la somma*/
q=riporto;/*l'operando q conterrà il riporto*/
}
return somma;
}

Screenshot esercizio 8
- 14 -

/*ESERCIZIO 9:
Scrivere una function C per eseguire la sottrazione aritmetica* binaria
(*: cioe' primo operando maggiore del secondo) di due numeri naturali
p e q (p, q, p-q•appartenenti a N) mediante gli operatori bitwise*/

#include<stdio.h>
int sottrazione(short p, short q);
void main()
{
short p,q;
printf("Inserisci il primo numero intero (p): ");
scanf("%hd",&p);
printf("\nInserisci il secondo numero intero (q): ");
/*Visto che facciamo la sottrazione, il primo operando deve essere maggiore del secondo*/
do
{
scanf("%hd",&q);
if(q>p)
{
printf("\nHai sbagliato reinserisci il numero intero q
(deve essere minore di p):\n");
}
}while(q>p);
printf("\nIl risultato della sottrazione e' (%hd)-(%hd): %hd",p,q,sottrazione(p,q));

int sottrazione(short p, short q)


{
short riporto,sottraz;
riporto=1;
/*Si cicla fino a quando non si ha un riporto maggiore di zero*/
while(riporto>0)
{
sottraz=p^q;/*qui si fa la sottrazione senza riporto*/
riporto=~p&q;/*si calcola solamente il prestito*/
riporto=riporto<<1;/*qui si porta il prestito a sinistra, facendolo shiftare di 1*/
p=sottraz;/*il primo operando conterra' la differenza*/
q=riporto;/*il secondo operando conterra' il riporto*/
}
return sottraz;
}

Screenshot esercizio 9
- 15 -

/*ESERCIZIO 10:
Scrivere una function C che, fissato il numero n di bit, calcoli la rappresentazione di un intero:
1)per complemento a 2;
2)eccesso B (B-biased) */

#include<stdio.h>
#include<math.h>
#define MAX_LEN 8
void stampa_bit(int bit[]);
void conv_quozerest(int n,int bit[]);
int complemento(int n);
int eccesso(int n);
void main()
{
int risult_compl,risult_ecc;
int i_min_compl,i_max_compl;
int i_min_ecc,i_max_ecc;
int bit[MAX_LEN];
int n;
/*Ricordiamo che il complemento a 2 si usa per memorizzare i numeri interi con segno:
ovviamente c'e' un intervallo non simmetrico di rappresentazione oltre il quale ci sono
problemi,arrecando risultati sbagliati*/
printf("\nIl range per complemento a 2 e' il seguente:\n ");
i_min_compl=-(pow(2,MAX_LEN-1));/*questo e' il valore minimo del range complemento: -2^(n-1)*/
i_max_compl=pow(2,MAX_LEN-1)-1;/*questo e' il valore massimo del range complemento: 2^(n-1)-1*/
printf("[%d][%d]",i_min_compl,i_max_compl);
/*Ricordiamo che la rappresentazione biased si usa per il campo esponente di un numero reale
floating point: ovviamente c'e' un intervallo non simmetrico di rappresentazione oltre il quale
ci sono problemi, arrecando risultati sbagliati*/
printf("\nIl range per eccesso biased e' il seguente:\n ");
i_min_ecc=-(pow(2,MAX_LEN-1)-1);/*il valore minimo del range eccesso B: -(2^(n-1)-1)) */
i_max_ecc=pow(2,MAX_LEN-1);/*questo e' il valore massimo del range eccesso B: 2^(n-1)*/
printf("[%d][%d]",i_min_ecc,i_max_ecc);
printf("\n\nInserisci il numero di cui vuoi fare la rappresentazione: ");
scanf("%d",&n);
printf("\nRappresentazione complemento a 2\n");/*Il complemento a 2 si comporta cosi':se il
numero inserito e' positivo allora il complemento coincide con il numero iniziale. Se il numero
inserito e' negativo allorail complemento vale (2^MAX_LEN-n) dove MAX_LEN indica il numero di
bit e n il numero inserito*/
/*qui facciamo il controllo se il numero inserito va al di fuori dell'intervallo*/
if(n>=i_min_compl&&n<=i_max_compl)
{
risult_compl=complemento(n);
conv_quozerest(risult_compl,bit);
stampa_bit(bit);
}
else
{
printf("Valore non consentito poiche' va oltre l'intervallo sopra mostrato");
}
/*ancora verifichiamo se andiamo al di fuori del range con il numero inserito*/
printf("\nRappresentazione biased\n");
if(n>=i_min_ecc&&n<=i_max_ecc)
{
risult_ecc=eccesso(n);
conv_quozerest(risult_ecc,bit);
stampa_bit(bit);
}
else
{
printf("Valore non consentito poiche' va oltre l'intervallo sopra mostrato");
}

}
- 16 -

int complemento(int n)/*Annotando che MAX_LEN e' il numero di bit e n il valore intero inserito,
usiamo la formula matematica per calcolare la rappresentazione
biased: essa e'[2^MAX_LEN+n]%2^MAX_LEN. Si potrebbe anche fare il complemento
a uno con l'operatore NOT bit a bit e poi sommare a 1 con resto
di 2^MAX_LEN*/
{
int potenza=pow(2,MAX_LEN);
n=(potenza+n)%potenza;
return n;
}

int eccesso(int n)/*Annotando che n e' il numero intero inserito, B il BIAS e MAX_LEN il numero di bit,
la rappresentazione biased di un intero si ottiene sommando il valore del BIAS
all'intero, ricordando che il BIAS vale (2^(MAX_LEN-1)-1), e quindi otteniamo:
n+B=n+(2^(MAX_LEN-1)-1)*/
{
int B;
B=pow(2,MAX_LEN-1)-1;
n=n+B;
return n;

void conv_quozerest(int n,int bit[])


{
short i=0,j;
/* Il ciclo e' ripetitivo e si interrompe se il quoziente sara' uguale a zero*/
/*il quoziente (n) inizialmente coincide con il numero che vogliamo invertire*/
do{
bit[i++] = n%2;/*Mediante il resto della divisione, costruiamo la stringa binaria*/
n=n/2;/*passiamo al quoziente successivo fino a che non e' terminato il ciclo*/
}while(n>0);
/*invertiamo le componenti dell'array*/
for(j=i;j<MAX_LEN;j++)
bit[j] = 0;
}

void stampa_bit(int bit[])


{
short i;
/*la stampa dei bit va dal piu' significativo al meno significativo:
cioe' se io ho il valore 2 ch in binario vale 0000 0010, i bit 0000
sono i piu' significativi e i bit 0010 sono i meno significativi*/
for(i=MAX_LEN-1; i>=0; i--)
{
(i%4 == 0) ? printf("%1d ",bit[i]) : printf("%1d", bit[i]);
}

}
Screenshot esercizio 10
- 17 -

/*ESERCIZIO 11:
Conoscendo la rappresentazione degli interi in C, riscrivere la function C per
l'addizione binaria di due interi (Z) mediante gli operatori bitwise,
traducendo l'algoritmo di seguito riportato:
*Algoritmo di "addizione binaria" mediante operatori sui bit
rip:=1;
while rip>0
sum := bitXOR(op1,op2);
rip := bitAND(op1,op2);
rip := leftSHIFT(rip,1);
op1 := sum; op2:=rip;
endwhile

Se l’operazione da implementare deve essere l’addizione algebrica (cioè deve valere anche per
gli interi negativi rappresentati per complemento a 2), quale accorgimento va usato nella
traduzione in C dell’algoritmo ... e perché.*/

#include<stdio.h>
short addizione(short p,short q);
void main()
{
short p,q,risultato;
printf("Inserisci qui il primo operando(scegli se neg o pos): ");
scanf("%hd",&p);
printf("\nInserisci qui il secondo operando(scegli se neg o pos): ");
scanf("%hd",&q);
risultato=addizione(p,q);
printf("\nIl risultato dell'addizione tra (%hd)+(%hd) e': %hd",p,q,risultato);

short addizione(short p,short q)


{
short somma;
short riporto=1;
/*In questo caso il riporto non deve essere maggiore di zero ma diverso da zero,
perche' se facessi (+7)+(-5) e seguendo l'algoritmo per l'addizione aritmetica,
essendo +7 e -5 rappresentati in complemento a due, su 4 bit avremo 0111+1011.
Si arrivera' ad un passaggio in cui il riporto sara' 1 con un risultato di 5 bit
cioe' 10010, ma il C vedrebbe il primo 1 come un numero negativo e uscirebbe dalla
condizione del while, senza finire l'algoritmo. Fatto cio', utilizzando valori positivi
e negativi, cioe' rappresentati con il complemento a 2, si ridurra' tutto ad una somma
algebrica.
Nel nostro caso: 0111+
1011=
-----
10010 e il primo 1 va eliminato ottenendo cosi' nella
rappresentazione di 4 bit il valore +2 cioe' 0010. Il risultato e' ora corretto*/

/*Il ciclo viene eseguito finchè non si ha un riporto uguale a zero*/


while(riporto!=0)
{
somma=p^q;/*qui facciamo la somma senza riporto*/
riporto=p&q;/*qui facciamo solamente il riporto*/
riporto=riporto<<1;/*qui portiamo il riporto a sinistra, facendolo shiftare di 1*/
p=somma;/*l'operando p conterrà la somma*/
q=riporto;/*l'operando q conterrà il riporto*/
}
return somma;
}
Screenshot esercizio 11
- 18 -

/*ESERCIZIO 12:
Scrivere una function C per visualizzare la rappresentazione binaria (s,e,m) di un numero float.
Verificare che il valore del numero ottenuto coincida con il dato iniziale */

#include<stdio.h>
#include<math.h>
#define BIAS 127/*127 e' il valore del BIAS in singola precisione*/
#define MAX_LEN 32 /*consideriamo la rappresentazione in singola precisione(32 bit) del float secondo
lo standard IEEE 754*/
void visualizza_bit(char bit[]);
float estrae_bit(int n,char bit[]);
void main()
{
char bit[MAX_LEN];
float verifica;
/*si usa la union sp(singola precisione) che contiene due variabili di tipo diverso
che condividono la stessa area di memoria: quando ci servira' di usare
la variabile float, nella lettura del valore reale da inserire, useremo il campo fn;
quando nella la funzione estrae(n,bit) dobbiamo usare i bitwise dobbiamo per forza usare
il campo in di tipo intero perche' solo con le varibili intere si possono usare i bitwise*/
union sp
{
float fn;
int in;
}n;
printf("Inserisci un numero di tipo reale(float): ");
scanf("%f",&n.fn);
verifica=estrae_bit(n.in,bit);
printf("\nBit corrispondenti SEGNO ESPONENTE MANTISSA");
visualizza_bit(bit);
printf("\n\nEcco la verifica: %f",verifica);
}

float estrae_bit(int n,char bit[])


{
int mask,segno,esponente,mantissa,estratto;
float numero;
float bit_implicito;
/*La maschera si usa per l'estrazione di alcuni bit che vengono mascherati con tutti 1*/
/*FACCIAMO L'ESTRAZIONE DEL SEGNO*/
mask = 0x80000000;/*costruisco la maschera considerando solamente il bit del segno in notazione
esadecimale*/
estratto = mask&n;/*estraggo il bit del segno facendo un AND tra la maschera e il valore
inserito in input*/
segno = (estratto >> 31);/*faccio shiftare il bit del segno all'estrema destra cosi' da poter
ottenere il valore del segno*/

/*FACCIAMO L'ESTRAZIONE DELL'ESPONENTE*/


mask=0x7f800000;/*costruisco la maschera considerando solamente i bit dell'esponente in
notazione esadecimale*/
estratto = mask&n;/*estraggo i bit dell'esponente facendo un AND tra la maschera e il
valore inserito in input*/
esponente=(estratto>>23);/*faccio shiftare i bit dell'esponente all'estrema destra cosi'
da poter ottenere il valore dell'esponente*/

/*FACCIAMO L'ESTRAZIONE DELLA MANTISSA*/


mask=0x007fffff;/*costruisco la maschera considerando solamente i bit della mantissa in
notazione esadecimale*/
mantissa=mask&n;/*estraggo i bit della mantissa facendo un AND tra la maschera e il
valore inserito da input*/
/*non c'e' bisogno di portare i bit della mantissa all'estrema destra perche'
gia' si trovono nella giusta posizione*/

bit_implicito=((float)mantissa/(1<<23))+1;/* Divido la mantissa per 2^23, in tal modo da


spostarmi la virgola e aggiungere l'hidden bit, ossia il bit implicito che non viene espresso
esplicitamente ma assume il valore convenzionale di 1*/

numero=pow(-1,segno)*(bit_implicito)*pow(2,esponente-BIAS);/*questa formula ci serve


per verificare che il numero ottenuto dalla rappresentazione coincida con il dato iniziale.
Essa e':(1)^s*[l.m]*2^e-(BIAS+1), dove -s- e' il segno, -l- e' il bit implicito,
-e- e' l'esponente*/
- 19 -

/*rappresentazione binaria del float n nell'array bit[MAX_LEN]


La rappresentazione va dal bit meno significativo al piu' significativo.
Quindi:
bit +signif. <----- bit -signif.
bit[0] bit[1] bit[2] ... bit[30] bit[31]*/
short i;
for(i=MAX_LEN-1;i>=0;i--)
{
bit[i]=(char)(1&n);/*qui facciamo l'estrazione dei bit meno significativi*/
n=n>>1;/*facciamo lo shift a destra di 1 bit per passare al bit successivo*/
}
return numero;
}

void visualizza_bit(char bit[])


{
short i;
printf("\n\t\t\t%1d ",bit[0]);/*stampo bit[0] che e’ il bit del segno*/
printf("\t");
/*Il for mi serve per stampare i bit dell'esponente che seguono al bit del segno*/
for(i=1;i<=8;i++)
{
printf("%1d",bit[i]);
}
printf("\t");
/*Questo for mi stampa i bit della mantissa che seguono ai bit dell'esponente*/
for(i=9;i<MAX_LEN;i++)
{
printf("%1d",bit[i]);
}
}

Screenshot esercizio 12

/*ESERCIZIO 14:
Scrivere delle function C per calcolare rispettivamente l’epsilon macchina del tipo float,
del tipo double e del tipo long double, visualizzando ad ogni passo i singoli bit.
Confrontare i risultati ottenuti con i valori delle variabili predefinite FLT_EPSILON,
DBL_EPSILON e LDBL_EPSILON.*/

#include<stdio.h>
#include<float.h>/*float.h ci consente di visualizzare le variabili predefinite FLT_EPSILON,
DBL_EPSILON e LDBL_EPSILON*/
#define MAX_LEN 80/*la massima lunghezza dell'epsilon macchina considerando la lunghezza long_double*/
void estrae_bit(short len, char ch[], char bit[]);
void stampa_bit_float(char bit[]);
void stampa_bit_double(char bit[]);
void stampa_bit_long_double(char bit[]);
void main()
{
short k;
char bit[MAX_LEN];
float epsilon_f;/*questo e' l'epsilon di tipo float*/
float epsilon1_f;/*questa e' la variabile di appoggio relativa all'epsilon di tipo float*/

double epsilon_d;/*questo e' l'epsilon di tipo double*/


double epsilon1_d;/*questa e' la variabile di appoggio relativa all'epsilon di tipo double*/

long double epsilon_ld;/*questo e' l'epsilon di tipo long double*/


long double epsilon1_ld;/*variabile di appoggio relativa all'epsilon di tipo long double*/
- 20 -

epsilon_f=1;/*epsilon float e’ inizializzato a 1*/


epsilon_d=1; /*epsilon double e’ inizializzato a 1*/
epsilon_ld=1; /*epsilon long double e’ inizializzato a 1*/

epsilon1_f=epsilon_f+1;/*la variabile di appoggio in singola precisione vale ep_f+1*/

epsilon1_d=epsilon_d+1;/*La variabile di appoggio in doppia precisione vale ep_d+1*/

epsilon1_ld=epsilon_ld+1;/*la variabile di appoggio in precisione long double vale ep_ld+1*/

/*L'uso del costrutto union e' dovuto al fatto che devo usare variabili intere
per la rappresentazione in binario*/
union sp/*UNION RELATIVA ALLA SINGOLA PRECISIONE*/
{
float f;
int n_f[1];
char c[4];
}x;

union dp/*UNION RELATIVA ALLA DOPPIA PRECISIONE*/


{
double d;
int n_d[2];
char c[8];
}y;

union long_d/*UNION RELATIVA ALLA PRECISIONE LONG DOUBLE*/


{
long double ld;
int n_ld[3];
char c[10];
}z;

x.f=epsilon1_f;/*nel campo float della union sp e' contenuta la variabile di appoggio in


singola precisione*/

y.d=epsilon1_d;/*nel campo double della union dp e' contenuta la variabile di appoggio in


doppia precisione*/

z.ld=epsilon1_ld;/*nel campo long double della union ldb e' contenuta la variabile di appoggio
in precisione long double*/

printf("PRECISIONE SINGOLA\n");
printf("Float x = %f\n",x.f);/*stampiamo il valore dell'epsilon macchina in singola precisione,
che in questo caso viene approssimato a 2*/

/*cicliamo per 23 volte perche' 23 e' il numero di bit della mantissa realmente rappresentati
in singola precisione*/
for(k=0;k<24;k++)
{
printf("\nn = %hd",k);
estrae_bit(sizeof(x.f),x.c,bit);
stampa_bit_float(bit);

/*L'epsilon macchina si trova effettuando delle divisioni successive per 2,


fin quando non trovo il piu'piccolo numero che sommato ad 1, mi da un valore maggiore
di 1*/
epsilon_f=epsilon_f/2;
epsilon1_f=epsilon_f+1;
x.f=epsilon1_f;
}
/*siamo usciti con un primo valore di epsilon che non da contributo quindi dobbiamo tornare
indietro*/
epsilon_f=2*epsilon_f;/*ho trovato l'epsilon, devo ripristinare l'ultima divisione*/
printf("\nVariabile predefinita FLT_EPSILON \t= %e\n",FLT_EPSILON);
printf("Variabile calcolata in singola precisione = %e\n",epsilon_f);

printf("\nDOPPIA PRECISIONE\n");
printf("Double y = %f\n",y.d);/*stampiamo il valore di epsilon in doppia precisione che in
questo caso viene approssimato a 2*/
- 21 -

for(k=0;k<53;k++)/*Cicliamo per 52 volte perche' 52 e' il numero di bit della mantissa


realmente rappresentati in doppia precisione*/
{
printf("\nn = %hd",k);
estrae_bit(sizeof(y.d),y.c,bit);
stampa_bit_double(bit);
/*L'epsilon macchina si trova effettuando delle divisioni successive per 2,
fin quando non trovo il piu'piccolo numero che sommato ad 1, mi da un valore maggiore
di 1*/
epsilon_d=epsilon_d/2;
epsilon1_d=epsilon_d+1;
y.d=epsilon1_d;
}
/*siamo usciti con un primo valore di epsilon che non da contributo quindi andiamo indietro*/
epsilon_d=2.0f*epsilon_d;/*ho trovato l'epsilon, devo ripristinare l'ultima divisione*/
printf("\nVariabile predefinita DBL_EPSILON \t= %e\n",DBL_EPSILON);
printf("Valore calcolato in doppia precisione = %e\n",epsilon_d);

printf("\nPRECISIONE LONG DOUBLE\n");


printf("Double y = %f\n",y.d);/*stampiamo il valore di epsilon in precisione long double che in
questo caso viene approssimato a 2*/

/*cicliamo per 63 volte perche' 63 e' il numero di bit della mantissa realmente
rappresentati in precisione long double*/
for(k=0;k<64;k++)
{
printf("\nn = %hd",k);
estrae_bit(sizeof(z.ld),z.c,bit);
stampa_bit_long_double(bit);

/*L'epsilon macchina si trova effettuando delle divisioni successive per 2, fin quando non
trovo il piu'piccolo numero che sommato ad 1, mi da un valore maggiore di 1*/
epsilon_ld=epsilon_ld/2;
epsilon1_ld=epsilon_ld+1;
z.ld=epsilon1_ld;
}
/*siamo usciti con un primo valore di epsilon che non da contributo quindi dobbiamo tornare
indietro*/
epsilon_d=2.0f*epsilon_d;/*ho trovato l'epsilon, devo ripristinare l'ultima divisione*/
printf("\nVariabile predefinita LDBL_EPSILON \t= %e\n",LDBL_EPSILON);
printf("Variabile calcolata in precisione long double = %e\n",epsilon_ld);
}

void stampa_bit_float(char bit[])


{
short k;
/*la stampa dei bit va dal piu' significativo al meno significativo:
in questo caso si cicla 32 volte visto che la rappresentazione del float e' in 32 bit*/
printf("\t");
for(k=31;k>=0;k--)
{
if(k==31||k==23)
printf("%1d ",bit[k]);
else
printf("%1d",bit[k]);
}
}

void stampa_bit_double(char bit[])


{
short k;
/*Per la stampa dei bit in questo caso si cicla 64 volte visto che 1 double = 64 bit*/
printf("\t");
for(k=63;k>=0;k--)
{
if(k==63||k==52)
printf("%1d ",bit[k]);
else
printf("%1d",bit[k]);
}
}
- 22 -

void stampa_bit_long_double(char bit[])


{
short k;
/*la stampa dei bit va dal piu' significativo al meno significativo:
in questo caso si cicla 80 volte visto che la rappresentazione del long double e' in 80 bit*/
printf("\t");
for(k=79;k>=0;k--)
{
if(k==79||k==64)
printf("%1d ",bit[k]);
else
printf("%1d",bit[k]);
}
}

void estrae_bit(short len,char ch[],char bit[])


{
short j,jc;
char c;
/*azzera tutte le componenti dell'array bit*/
for(j=0; j<MAX_LEN; j++)
bit[j]=0;
/*Questo ciclo avanza sul numero di byte e,
nel caso del char viene eseguito 1 volta,
nel caso dello short viene eseguito 2 volte*/
for(jc=0; jc<len; jc++)
{
/*memorizza l'informazione individuata dall'i-esimo byte*/
c=ch[jc];/*Ricava cio' che trovi al byte i*/
/*scorri i bit del byte preso in esame*/
for(j=0;j<8;j++)
{
bit[j+8*jc] =c&1;/*individuando byte e bit, inserisce il valore del primo bit*/
/*Ci sono due indirizzamenti:uno sul bit del byte attuale che e' la variabile j,
l'altro jc indica il byte corrispondente e quindi moltiplicato per 8*/
c=c>>1;/*sposta di un posto per controllare il bit successivo*/
}
}
}
Screenshot esercizio 14
- 23 -

/*ESERCIZIO 18
Scrivere una function C per calcolare una somma di molti addendi dello stesso ordine di grandezza. A
scelta la versione
iterativa o ricorsiva dell'algoritmo di somma a blocchi. Applicare l'algoritmo al particolare problema
test di cui e' gia' nota la soluzione.
Ecco il problema:
10^8
---
\
/ 10^-6 ---->soluzione del problema= 100
---
k=1
Ricordiamo che 10^8=100000000 e 10^-6=0,000001*/

#include <stdio.h>
#include <stdlib.h>
#include<math.h>
float somma_blocchi(unsigned int N, float A[]);
void main()
{
float somma;/*la variabile somma e' quella in cui viene collocato il risultato*/
unsigned int k;/*k e' un contatore che ci serve per fare delle iterazioni*/
unsigned int n=pow(10,8);/*n e' il numero di somme da effettuare*/
float *addendi;/*variabile vettore in cui vengono collocati gli addendi da sommare*/
float num=pow(10,-6); /*numero reale da sommare*/
addendi=(float*)malloc(sizeof(float)*n);/*Allochiamo dinamicamente attraverso la funzione
malloc perche' e' richiesto molto spazio. Malloc alloca un blocco di memoria della dimensione
richiesta, in tal caso 10^8, e ne ritorna il puntatore. Se non c'e' memoria disponibile ritorna
NULL. La dimensione massima del blocco di memoria che e' possibile allocare con questa funzione
dipende dal processore, dalla memoria centrale e dal sistema operativo in uso*/

/*inseriamo gli addendi nel vettore*/


for(k=0;k<n;k++)
{
addendi[k] = num;/*gli addendi che vengono inseriti nel vettore hanno lo
stesso ordine di grandezza e valore di num*/
}
somma=somma_blocchi(n,addendi);
printf("Numero reale da sommare= %f(10^-6)",num);
printf("\nNumero di somme da effettuare= %u(10^8)",n);
printf("\n\nSomma in notazione esponenziale : %e",somma);
printf("\nSomma in notazione reale : %f",somma);
}

/*l'algoritmo della somma a blocchi si comporta cosi': partendo da un vettore gia' ordinato,
invece di sommare le componenti adiacenti dell'array, somma quelle diametralmente
opposte, cioe' simmetriche rispetto al centro*/
float somma_blocchi(unsigned int N, float A[])
{
unsigned int k,Nmez;

while(N>1)
{
/* Dividi N e fai NMEZ somme. Il primo con l'ultimo, secondo col penultimo... */
Nmez=N/2;
for(k=0;k<Nmez;k++)
{
A[k]=A[k]+A[N-1-k];
}
/* Se N e' dispari allora N=Nmez+1 altrimenti l'elemento di mezzo non considerato tra
le N Dispari somme, si perde.*/
if(N%2 == 0)
{
N=Nmez;
}
else
{
N=Nmez+1;
}

}
- 24 -

/* La somma lavora in place, conservando le somme sui k cicli. L'ultimo elemento


sara' la somma totale*/
return A[0];
}
Screenshot esercizio 18

/*ESERCIZIO 19:
Scrivere una function C per calcolare iterativamente la somma che segue con il
criterio di arresto naturale.
n
------
\
/ (x^k)/k! che vale circa e^a dove -e- e' il numero di Nepero
------
k=1 */

#include <stdio.h>
#include <float.h>/*mi serve per avere il valore preciso dell'epsilon macchina*/
#include<math.h>/*mi serve per fare l'elevazione a potenza con pow*/
float arresto(float x, float somma,float k);
float fattoriale(float k);
void main()
{
float k=1.0f;
float x,somma=0.0f;
const float e=2.718281828;
printf("FLT_EPSILON vale: %e",FLT_EPSILON);
printf("\nFLT_EPSILON/2 vale: %e",FLT_EPSILON/2);
printf("\nInserire valore A: "); scanf("%f",&x);
somma= arresto(x, somma,k);
printf("\nSomma con criterio di arresto naturale: %e", somma);
printf("\ne^%f vale: %f",x,pow(e,x));
}

/*Il criterio di arresto naturale e' un criterio che ci consente di arrestare la somma
per evitare somme inutili di addendi non significativi rispetto alla somma.
Possiamo avere l'approssimazione di ULP(S)=S*Emach/2 dove ULP indica UNIT IN THE LAST
PLACE di S che si definisce come il minimo numero floating-point positivo che sommato ad S
fornisce un risultato maggiore di S, cioè da contributo ad S*/
float arresto(float x, float somma,float k)
{
float rapporto;
/*Il ciclo while itera fin quando il rapporto: (x^k)/k!
non diventa piu' piccolo di ULP(S) e , quindi, non dara' piu' contributo alla somma*/
while (rapporto>((somma)*FLT_EPSILON*0.5))
{
rapporto=pow(x,k)/(fattoriale(k));
somma = somma + rapporto;/*facciamo la somma incrementale*/
k++;/*incrementiamo il valore di k dopo ogni somma*/
}
return somma;
}
- 25 -

/*questa e' una funzione ricorsiva che mi calcola il fattoriale, che viene calcolato a
sua volta come k=k*(k-1)*(k-2)*... */
float fattoriale(float k)
{
if(k<=1)
{
return 1;
}
else
{
return k*fattoriale(k-1);
}

}
Screenshot esercizio 19

/*ESERCIZIO 20
Scrivere una function C per calcolare la somma di addendi ordinati; sommare gli
addendi una volta in ordine crescente ed un'altra in ordine decrescente.

-----------------------------> ORDINE DECRESCENTE


Somma=1+1/2^2+1/3^2+1/4^2+...+1/n^2 Il risultato e' approssimabile a (pi^2/6)
<---------------------------- ORDINE CRESCENTE
Mostrare qual è il modo migliore di sommare in questo caso.*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>// mi serve per definire la costante pi_greco = M_PI = 3.14 e per pow()
#define MAX_DIM 50
float somma_crescente(int n);
float somma_decrescente(int n);
double errore_relativo(double x_appross, float flx);
int main()
{
float Sum_Cre, Sum_Dec;
double x_appross = (M_PI*M_PI)/6; /*La successione e' approssimabile a pi_greco^2/6*/
double Er_rel_cre, Er_rel_dec;
int n;

printf("Inserisci il numero di somme: ");


scanf("%d", &n);
/*____ CALCOLO SOMMA CRESCENTE E DECRESCENTE CON RELATIVI ERRORI RELATIVI ___*/
Sum_Cre = somma_crescente(n);
Sum_Dec = somma_decrescente(n);
Er_rel_cre = errore_relativo(x_appross, Sum_Cre);
Er_rel_dec = errore_relativo(x_appross, Sum_Dec);
- 26 -

/*Come si potra' notare dalla stampa dei risultati


i valori ottenuti denotano che sommando i termini in ordine decrescente si ottiene un errore
relativo costante a differenza della somma in ordine crescente dove il risultato dell'errore
relativo e' di massima accuratezza*/

/*Da questo si evince che la somma crescente e' piu' accurata perche' ha un valore di errore
sicuramente minore rispetto a quello della somma decrescente nel momento in cui si fanno somme
di n numeri dove n si presenta come un valore dal 5000 in poi.
Il problema dell'errore relativo decrescente e' dovuto al fatto che, da una certa iterazione in poi,
l'n-esimo termine della uccessione avra' avuto valore inferiore all'ULP dell'attuale somma parziale,
non dando cosi'alcun contributo al risultato che invece sarebbe dovuto essere diverso. */

printf("\nValore appross(pi^2/6)= %e!\nSum Cre= %e!\nSum Dec= %e!\nErrore relativo cre= %e\nErrore
relativo dec= %e", x_appross,Sum_Cre, Sum_Dec, Er_rel_cre, Er_rel_dec);

return 0;
}

/* Somma crescente somma da 1/n^2 a 1 */


float somma_crescente(int n)
{
int i;
float sum=0.0f;
for (i=n; i>=1; i--)/*sommiamo a partire dal numero di valore piu' piccolo: (1/n^2) fino
al numero di valore piu' grande cioe' 1*/
sum=sum+(1.0/(pow(i,2)));
return sum;
}

/* Somma decrescente somma da 1 a 1/n^2 */


float somma_decrescente(int n)
{
int i;
float sum=0.0f;
for (i=1; i<=n; i++)/*sommiamo a partire dal numero di valore piu' grande: 1 fino
al numero di valore piu' piccolo cioe' (1/n^2)*/
sum=sum+(1.0/(pow(i,2)));
return sum;
}

double errore_relativo(double x_appross,float flx)/*da un punto di vista matematico


l'errore relativo che ci consente di avere
cifre significative corrette e' dato dal rapporto
tra l'errore assoluto e il valore x che si vuole considerare*/
{
return fabs(flx-x_appross)/(fabs(flx));

}
Screenshot esercizio 20
- 27 -

/*ESERCIZIO 22 QUIZ: sono stati proposti 2 esercizi dei quali esplicitare il funzionamento e le
differenze principali. Gli esercizi proposti sono i seguenti*/

PRIMO ESERCIZIO
#include<stdio.h>
#include<string.h>
/*Questo programma e' funzionante poiche':
 si dichiara una stringa ch che conterra' i singoli caratteri della stringa costante
 in questo caso si puo' modificare il puntato della prima cella del vettore*/
void main()
{
char stringa[]="ciao";/*questa e' la dichiarazione di una stringa, cioe' una
sequenza di caratteri*/
puts(stringa);/*puts ci consente di visualizzare su schermo la stringa dichiarata*/
*stringa='m';/*il puntatore stringa contiene il carattere costante 'm', che dopo la
stampa della stringa, va a sostituire il primo carattere della stringa,
eliminando il precedente*/
puts(stringa);/*ristampiamo nuovamente la stringa facendola visualizzare*/
}
Screenshot esercizio 22 quiz-1

SECONDO ESERCIZIO
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*la gestione mediante puntatori e' quella ottimale ripetto alla dichiarazione di array
di carattere. Tuttavia in questo caso avviene una segnalazone diagostica:
qui c'e' un puntatore che punta in memoria all'indirizzo base di una stringa costante
che non puo' essere modificata*/

void main()
{
char *stringa="ciao";/*il puntatore stringa contiene la stringa costante ciao*/
puts(stringa);/*stampiamo la stringa per visualizzarla*/
*stringa='m';/*questa e' la parte del codice che mi da errore*/
puts(stringa);/*ristampiamo la stringa*/
}

/*ESERCIZIO 23
Confrontando i risultati con quelli delle relative funzioni del C ed utilizzando per le stringhe:
-l’allocazione statica
-l’allocazione dinamica
scrivere una function C che accetti in input il numero n e legga da tastiera n caratteri (uno alla
volta)costruendo la stringa che li contiene (parametro di output), senza usare strcat(...).*/
/*Ricordiamo che l'allocazione dinamica rispetto all'allocazione statica consente un risparmio di
Memoria, permettendo di allocare o deallocare spazio durante l'esecuzione del programma*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 50 /*questo e' il numero massimo di caratteri inseribili nella stringa*/
void dinamic_string(char *vett,int n);
void static_string(char vettore[],char risultato[],int n);
void main()
{
int n;/*n indica il numero di caratteri da inserire da tastiera*/
int i;
char vettore [MAX], risultato[MAX];/*variabili relativa all'allocazione statica*/
char *vett;/*questa variabile mi serve per l'allocazione dinamica*/
printf("Quanti caratteri vuoi inserire? (max 50): ");
scanf("%d",&n);
static_string(vettore,risultato,n);
dinamic_string(vett,n);
}
- 28 -

void static_string(char vettore[],char risultato[],int n)


{
int i,j=0;
printf("\n*****ALLOCAZIONE STATICA*****\n");
printf("\nInserisci carattere per carattere nel vettore: \n");
for(i=0;i<n;i++)
{
printf("%d-simo carattere--> ",i);
fflush(stdin);/*puliamo il buffer della tastiera per consentire l'inserimento*/
scanf("%c",&vettore[i]);
}
vettore[n]='\0';/*chiudiamo la stringa dando all'ultimo elemento il valore di backslash 0*/
printf("\n\nIl vettore ha questi caratteri:");/*facciamo visualizzare i caratteri inseriti*/
for(i=0;i<n;i++)
{
printf("%c ",vettore[i]);
}

/*inseriamo uno per volta i caratteri nella stringa finale per ottenere la concatenazione*/
for(i=0;i<n;i++)
{
risultato[i]=vettore[i];
}
risultato[n]='\0';/*chiudo la stringa*/
printf("\nStringa concatenata: %s",risultato);
}

void dinamic_string(char *vett, int n)


{
int i;
printf("\n\n*****ALLOCAZIONE DINAMICA*****\n");
/*la malloc in generale alloca memoria e restituisce un puntatore al blocco di memoria
allocato oppure NULL se la memoria disponibile è insufficiente. */
vett=(char *)malloc(n);/*assegnamo un blocco di memoria di n a vett*/
if(vett==NULL)
{
printf("\nMemoria insufficiente, fine del programma");
}

printf("\nInserisci ora carattere per carattere nel vettore: \n");


for(i=0;i<n;i++)
{
printf("%d-simo carattere--> ",i);
fflush(stdin);/*puliamo il buffer della tastiera per consentire l'inserimento*/
*(vett+i)=getchar();
}
*(vett+i)='\0';/*chiudiamo la stringa con backslash 0 : '\0'*/
fflush(stdin);
printf("\nStringa concatenata: %s ",vett);
free(vett);/*rilascia il blocco di memoria precedentemente riservato a vett con malloc*/

}
Screenshot esercizio 23
- 29 -

/*ESERCIZIO 24
Confrontando i risultati con quelli delle relative funzioni del C ed utilizzando per le stringhe:
-l’allocazione statica
-l’allocazione dinamica
scrivere una function C che restituisca la concatenazione di due stringhe (parametri di input)
senza usare strcat(...). È a scelta restituire la concatenazione delle due stringhe in una terza
variabile (parametro di output o function stessa) oppure nella prima delle due variabili di input.*/
/*Ricordiamo che l'allocazione dinamica rispetto all'allocazione statica consente un risparmio di
memoria consentendo di allocare o deallocare spazio durante l'esecuzione del programma*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 50/*si possono inserire max 50 caratteri in ogni stringa per un totale finale di 100*/
void dinamic_string(char *stringa1, char *stringa2, char *stringa_risult,int n);
void static_string(char stringa_1[], char stringa_2[],char stringa_conc[]);
void main()
{
char stringa_1[MAX],stringa_2[MAX],stringa_conc[2*MAX];/variabili di allocazione statica*/
char *stringa1, *stringa2, *stringa_risult;/*variabili di allocazione dinamica*/
int n;/*questo e' il numero di caratteri che voglio inserire per costruire le due stringhe*/
printf("Inserisci quanti caratteri vuoi mettere(max 50) nelle due stringhe: ");
scanf("%d",&n);
fflush(stdin);/*puliamo il buffer della tastiera per non visualizzare caratteri sporchi*/
static_string(stringa_1,stringa_2,stringa_conc);
dinamic_string(stringa1,stringa2,stringa_risult,n);

void static_string(char stringa_1[], char stringa_2[], char stringa_conc[])


{
int i, n, j=0;
printf("\n*****ALLOCAZIONE STATICA*****");
printf("\n\nDigita ora la prima stringa: ");
gets(stringa_1);/*gets e' il modo piu' semplice per leggere una stringa da tastiera*/
printf("La lunghezza della stringa 1: %d",strlen(stringa_1));
printf("\n\nDigita ora la seconda stringa: ");
gets(stringa_2);
printf("La lunghezza della stringa 2: %d",strlen(stringa_2));

printf("\n\nLa stringa 1 e': %s\n",stringa_1);


printf("La stringa 2 e': %s\n",stringa_2);

n=strlen(stringa_1); /*lunghezza della stringa_1 che coincide con stringa2*/

for (i=0; i<n; i++) /*inserisce la stringa_1 nell'array stringa_conc*/


{
stringa_conc[i]= stringa_1[i];
}

/*Inserimento della stringa2 nell'array stringa_conc; i caratteri della stringa_2 vanno inseriti
nell'array, a partire dalla posizione di indice i=n (indice che corrisponde alla posizione
nell'array immediatamente successiva alla stringa_1 già inserita), fino ad i<n+n ovvero
fin quando non ha copiato tutta la seconda stringa avente lunghezza n*/
for (i=n; i<2*n; i++)
{
stringa_conc[i]=stringa_2[j];
j++;/*incrementiamo il contatore j fino a che non sono stati inseriti tutti i caratteri*/
}
stringa_conc[n+n]='\0'; /*chiudiamo la stringa con il backslash 0, il terminatore di fine stringa*/
printf("\nStringa concatenata: %s",stringa_conc);
}
- 30 -

void dinamic_string(char *stringa1, char *stringa2, char *stringa_risult,int n)


{
int i,j=0;
printf("\n\n*****ALLOCAZIONE DINAMICA*****");
/* consideriamo n/2 perche' n vale 10 ed e' il size delle due stringhe insieme, quindi per una
stringa abbiamo come allocazione e iterazione n/2*/
/*la malloc in generale alloca memoria e restituisce un puntatore al blocco di memoria
allocato oppure NULL se la memoria disponibile è insufficiente. */
stringa1=(char *)malloc(n/2+1);/*assegno un blocco di memoria di n/2 + 1 per ‘\0’ a stringa1*/
if(stringa1==NULL)
{
printf("\nMemoria insufficiente, fine del programma");
}
stringa2=(char *)malloc(n/2);/*assegno un blocco di memoria di n/2 + 1 per ‘\0’ a stringa2*/
if(stringa2==NULL)
{
printf("\nMemoria insufficiente, fine del programma");
}
stringa_risult=(char *)malloc(n);/*assegniamo un blocco di memoria di n+1(perche' dobbiamo
considerare la lunghezza di stringa1 e stringa2, incluso ‘\0’ ) a stringa_risult*/
if(stringa_risult==NULL)
{
printf("\nMemoria insufficiente, fine del programma");
}
printf("\n\nDigita ora la prima stringa: ");
fflush(stdin);
gets(stringa1);/*gets e' il modo piu' semplice per leggere una stringa da tastiera*/
printf("La lunghezza della stringa 1: %d",strlen(stringa1));
printf("\n\nDigita ora la seconda stringa: ");
fflush(stdin);
gets(stringa2);
printf("La lunghezza della stringa 2: %d",strlen(stringa2));
printf("\n\nLa stringa 1 e': %s\n",stringa1);
printf("La stringa 2 e': %s\n",stringa2);
for(i=0;i<n/2;i++)/*inseriamo la stringa1 nell'area di memoria di stringa_risult*/
{
*(stringa_risult+i)=*(stringa1+i);
}
/*Inseriamo la stringa2; i caratteri della seconda stringa vanno inseriti nell'area di
memoria a partire dalla locazione di indice i=n/2 (locazione che corrisponde alla
posizione immediatamente successiva alla prima stringa gia' inserita), fino a n(size delle due
stringhe unite) cioe' fin quando non ha copiato tutta la seconda stringa avente lunghezza n/2*/
for(i=n/2;i<n;i++)
{
*(stringa_risult+i)=*(stringa2+j);
j++;/*incrementiamo il contatore j fino a che non sono stati inseriti tutti i
caratteri*/
}
*(stringa_risult+i)='\0';/*chiudiamo la stringa*/
fflush(stdin);
printf("\nStringa concatenata: %s ",stringa_risult);
free(stringa1);/*rilascia il blocco di memoria prima riservato a stringa1 con malloc*/
free(stringa2);/*rilascia il blocco di memoria prima riservato a stringa2 con malloc*/
free(stringa_risult);/*rilascia il blocco di memoria prima riservato a stringa_risult*/
}
Screenshot esercizio 24
- 31 -

/*ESERCIZIO 25:
Confrontando i risultati con quelli delle relative funzioni del C, scrivere una function C
che restituisca la prima occorrenza di una sottostringa in una stringa senza usare strstr(...).*/

#include <stdio.h>
#include <string.h>
#define MAX_LEN 50
int cerca(char chiave[],char testo[]);
void main()
{
char testo[MAX_LEN];/*questo e' il testo di partenza*/
char chiave[MAX_LEN];/*questo e' il testo da ricercare e ha una massima lunghezza pari
alla lunghezza del testo di partenza*/
int n_occorrenze;
printf("\nInserisci qui il testo di partenza: ");
gets(testo);
printf("\n\t\t\t\t\t 0123456789012345678901234");/*questi numeri servono a riferimento per il
lettore per capire meglio la posizione dei caratteri*/
printf("\nIl testo di partenza inserito e' questo: %s",testo);
printf("\nLa lunghezza del testo e' %d",strlen(testo));
fflush(stdin);
printf("\n\nInserisci qui il testo da ricercare: ");
gets(chiave);
fflush(stdin);
printf("Ecco il testo che hai inserito: %s",chiave);
fflush(stdin);
printf("\nQuesta e' la lunghezza del testo da cercare: %d",strlen(chiave));
n_occorrenze=cerca(chiave,testo);
printf("\n\nLa stringa *%s* appare %d volte nel testo:\n-%s-\n",chiave,n_occorrenze,testo);
}

int cerca(char chiave[],char testo[])


{
int i=0;
int conta_chiave=0;/*questa variabile mi conta il numero di occorrenze*/
int n=strlen(chiave);/*lunghezza del testo da cercare*/
int m=strlen(testo);/*lunghezza del testo iniziale*/
/*il corpo del ciclo esamina tutte le sottostringhe di lunghezza n, cioe' la lunghezza del
testo da ricercare, contenute nella stringa testo. Ognuna di queste sottostringhe viene
confrontata con la sottostringa da ricercare*/
for(i=0;i<m-n;i++)
{
if(strncmp(chiave,&testo[i],n)==0)/*strncmp restituisce 0 se la sottostringa coincide
con una parte della stringa: allora vuole dire che la sottostringa e' stata trovata*/
{
conta_chiave++;/*incrementa il numero di volte che trova la sottostringa
nella stringa*/
}
}
return conta_chiave;/*ritorniamo il numero di occorrenze*/
}
Screenshot esercizio 25
- 32 -

/*ESERCIZIO 26: Usando l’allocazione dinamica e le funzioni C per manipolare le stringhe,


scrivere una function C che restituisca il numero totale delle occorrenze
di una sottostringa in una stringa e ne visualizzi la posizione di tutte le occorrenze trovate.*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void cerca(char *testo, char *patt, int len_t, int len_p);
void main()
{
char *testo;/*questo e' il puntatore alla stringa costante iniziale*/
char *patt;/*questo e' il puntatore alla sottostringa costante da ricercare*/
int len_t, len_p;
testo=(char *)malloc(100);/*allochiamo dinamicamente spazio per inserire il testo principale*/
printf("Immetti il testo principale: ");
gets(testo);
len_t=strlen(testo);/*leggiamo la lunghezza del testo principale senza considerare ‘\0’*/
patt=(char *)malloc(100);/*allochiamo dinamicamente spazio per inserire la sottostringa*/
printf("\nImmetti il pattern da ricercare: ");
gets(patt);
len_p=strlen(patt);/*leggiamo la lunghezza del pattern senza considerareil’\0’*/
printf("\nCerca pattern *%s* di lunghezza %d nel \ntesto principale che ha lunghezza %d ed e'
questo:\n0123456789012345...\n%s",patt,len_p,len_t,testo);
cerca(testo,patt,len_t,len_p);
}

void cerca(char *testo, char *patt, int len_t, int len_p)


{
char *te;/*questa variabile mi consente di avanzare nel testo nel corso della ricerca*/
char *ch;/*questa variabile temporanea da il risultato del confronto testo-pattern*/
char posizioni[50];/*questo e' l'array che mi indica la posizione delle occorrenze*/
int i;
int occorrenze=0; /*non ci sono occorrenze prima dello scorrimento del testo*/
te=testo;/*si inizializza il puntatore te con indirizzo base del testo*/
/*Il do-while ricerca all'interno del testo che parte dalla posizione indicata da ‘te’*/
do{
ch=strstr(te,patt);/*strstr e' una funzione di manipolazione che confrontando il testo
con pattern, restituisce un puntatore alla prima occorrenza del pattern nel testo se il
dato e' stato trovato, invece se non ha trovato nulla ritorna NULL*/
if(ch!=0)/*se il dato e' stato trovato*/
{
posizioni[occorrenze]=ch-testo;/*l'array posizioni parte dalla posizione 0 nel
testo principale e quando viene incrementato il valore di occorrenze ad ogni
incremento l'array arrivera' alla posizione in cui e' stato trovato il pattern*/
occorrenze++;/*incrementa il numero di occorrenze*/
te=ch+len_p;/*sposta il puntatore all'inizio del testo di tanti caratteri quanto
e' lungo il pattern trovato cioe' avanza sul testo saltando il pattern che e'
stato appena trovato*/
}
}while(te<testo+len_t&&ch!=0);/*il tutto viene ripetuto finche' ch e' diverso da 0 cioe'
finche' non ha trovato il pattern nel testo e finche' c'e' ancora spazio sufficiente per
cercare il pattern cioe' te e' minore di testo+len_t*/
printf("\n\nIl numero di occorrenze trovate: %d",occorrenze);
printf("\nIl pattern e' stato trovato in queste posizioni:\n");
/*in base al numero di occorrenze trovo le posizioni della prima fino alla n-sima occorrenza*/
for(i=0;i<occorrenze;i++)
{
printf("%d ",posizioni[i]);
}
free(testo);/*libera lo spazio precedentemente allocato da testo*/
free(patt);/*libera lo spazio precedentemente allocato dal pattern*/
}
Screenshot esercizio 26
- 33 -

/*ESERCIZIO 29:
A partire dalla matrice A(mxn), del tipo sotto indicato, allocata per righe:
-staticamente,
-dinamicamente
visualizzarne gli elementi per colonne:

11 12 13 14 15 16
21 22 23 24 25 26------>A[4][6]-->[4 righe]x[6 colonne]
31 32 33 34 35 36
41 42 43 44 45 46
Gli elementi a[i][j] della matrice sono tali che le unita' indicano la colonna e
le decine indicano la riga cui l'elemento appartiene*/

#include <stdio.h>
#include <stdlib.h>
#define M 4 /*questo e' il numero di righe*/
#define N 6 /*questo e' il numero di colonne*/
void alloc_static(int a[][]);
void alloc_dinamic(int *pa,int a[][]);
void main()
{
int a[M][N];/*questa variabile mi serve per l'allocazione statica*/
int *pa;/*questa variabile mi serve per l'allocazione dinamica*/
alloc_static(a);
alloc_dinamic(pa,a);
}

void alloc_static(int a[][])


{
int i,j;/*contatori*/
int valore=0;/*ci consente di inserire automaticamente i valori all'interno della matrice MxN*/
printf("\n*****ALLOCAZIONE STATICA*****\n\n");
printf("Visualizzazione per righe(metodo classico):\n");
for (i=0;i<M;i++)
{
valore=(i+1)*10;/*creiamoci qui i valori 10,20,30 e 40*/
for(j=0;j<N;j++)
{
valore++;/*incrementiamo di 1 il valore di valore cosi' da avere
10+1=11,11+1=12, sulla 1 riga,

20+1=21,21+1=22, sulla 2 riga,

30+1=31,31+1=32, sulla 3 riga,

40+1=41,41+1=42, sulla 4 riga*/


a[i][j]=valore;/*riempiamo la matrice con i valori di valore*/
printf("%d\t",a[i][j]);
}
printf("\n");
}
printf("\nVisualizzazione per colonne(nella traccia):\n");
/* Inverto gli indici nell'array, ricordando di cambiare anche M ed N nei cicli for*/
for (i=0;i<N;i++)
{
for(j=0;j<M;j++)
{
printf("%d\t",a[j][i]);
}
printf("\n");
}

}
- 34 -

void alloc_dinamic(int *pa,int a[][])


{
int i,j;
/*Utilizzo di malloc per allocazione dinamica*/
pa=malloc(M*N*sizeof(int));/*ricordiamo che 1 int=4 byte*/
printf("\n*****ALLOCAZIONE DINAMICA*****\n\n");
printf("Visualizzazione per righe(metodo classico): \n");
for (i=0;i<M;i++)
{
for(j=0;j<N;j++)
{
*(pa+i*N+j)=a[i][j];/*eseguo l'allocazione dinamica per righe*/
printf("%d\t",*(pa+i*N+j));
}
printf("\n");
}

printf("\nVisualizzazione per colonne(nella traccia): \n");


/*Per stampare la matrice dinamica per colonne
inverto semplicemente i due cicli for,
indipendentemente da come alloco in memoria*/
for (i=0;i<N;i++)
{
for(j=0;j<M;j++)
{
*(pa+j*M+i)=a[j][i];/*eseguo l'allocazione dinamica per colonne*/
printf("%d\t",*(pa+j*M+i));

}
printf("\n");
}
}

Screenshot esercizio 29
- 35 -

/*ESERCIZIO 30
Scrivere una function C che restituisca la matrice C prodotto righexcolonne [vedi pdf delle dispense]
di due matrici rettangolari A e B le cui dimensioni sono stabilite in input (usare per tutte le matrici
l'allocazione dinamica e generarle come numeri reali random). C'e' qualche preferenza nell'usare
malloc() o calloc() rispettivamente per A, B o C? Verificare se i tempi di esecuzione, per la sola
allocazione e totali, sono gli stessi*/

/*Ricordiamoci che per fare il prodotto righexcolonne di due matrici rettangolari bisogna seguire
questa formula: C[M][N]=A[M][P]xB[P][N]
Da questa formula vediamo che:
M e' il numero di righe della matrice finale, ma anche il numero di righe di A
P e' il numero di colonne di A ma anche il numero di righe di B
N e' il numero di colonne della matrice finale, ma anche il numero di colonne di B*/

#include<stdio.h>
#include<stdlib.h>/*stdlib ci serve per le funzioni di allocazione malloc e calloc
ma anche per generare numeri casuali con rand() e srand()*/
#include<time.h>/*ci serve per calcolare i tempi di esecuzione con malloc e calloc attraverso time()*/
#include<windows.h>/*questa libreria mi serve per usare la funzione per il tempo
QueryPeformanceCounter,che e' solo per Windows ed e' accurata ai nanosecondi*/
void matrice_A(int *a, int m, int p, int r, int f);
void matrice_B(int *b, int p, int n, int r, int f);
void matrice_C(int *a, int *b, int *c, int m, int n,int p);
void main()
{
int *a, *b, *c;
int f=1,r=10;/*questi sono gli estremi dell'intervallo dei valori che voglio generare random*/

int m,p,n;

LARGE_INTEGER ticksPerSecond, TICKS1, TICKS2;


double elapsed_Time1, elapsed_Time2;/*la prima variabile e' relativa al tempo di calloc,
l'altra di malloc*/
QueryPerformanceFrequency(&ticksPerSecond);//processor clock frequency

srand((unsigned)time(NULL));/*questa istruzione mi consente di generare automaticamente numeri


casuali*/
printf("\nInserisci qui il numero di righe della matrice A: ");
scanf("%d",&m);
printf("\nInserisci qui il numero di colonne della matrice A\nche coincide con il numero di
righe di B: ");
scanf("%d",&p);
printf("\nInserisci qui il numero di colonne di B: ");
scanf("%d",&n);
/*qui facciamo l'allocazione dinamica delle 3 matrici con calloc*/
QueryPerformanceCounter(&TICKS1);//tempo iniziale
a=(int*)calloc(m*p,sizeof(int));
b=(int*)calloc(p*n,sizeof(int));
c=(int*)calloc(m*n,sizeof(int));
QueryPerformanceCounter(&TICKS2);//tempo finale
printf("\n######################ALLOCAZIONE CON CALLOC#####################\n");
/*controllo se le matrici sono state allocate correttamente*/
if(a==NULL||b==NULL||c==NULL)
{
fprintf(stderr,"\n!!! Allocazione fallita !!!");
exit(EXIT_FAILURE);
}
else
{
printf("\n!!! Allocazione di int A[%d][%d] riuscita !!!",m,p);
printf("\n!!! Allocazione di int B[%d][%d] riuscita !!!",p,n);
printf("\n!!! Allocazione di int C[%d][%d] riuscita !!!",m,n);
}
elapsed_Time1=(double)(TICKS2.QuadPart-TICKS1.QuadPart)/(double)ticksPerSecond.QuadPart;/*il
tempo impiegato da calloc e' dato dalla differenza tra il tempo finale e quello inziale*/
- 36 -

/*qui facciamo l'allocazione dinamica delle 3 matrici con malloc*/


QueryPerformanceCounter(&TICKS1);//tempo iniziale
a=(int*)malloc(m*p*sizeof(int));
b=(int*)malloc(p*n*sizeof(int));
c=(int*)malloc(m*n*sizeof(int));
QueryPerformanceCounter(&TICKS2);//tempo finale
printf("\n######################ALLOCAZIONE CON MALLOC#####################\n");
/*controllo se le matrici sono state allocate correttamente*/
if(a==NULL||b==NULL||c==NULL)
{
fprintf(stderr,"\n!!! Allocazione fallita !!!");
exit(EXIT_FAILURE);
}
else
{
printf("\n!!! Allocazione di int A[%d][%d] riuscita !!!",m,p);
printf("\n!!! Allocazione di int B[%d][%d] riuscita !!!",p,n);
printf("\n!!! Allocazione di int C[%d][%d] riuscita !!!",m,n);
}
elapsed_Time2=(double)(TICKS2.QuadPart-TICKS1.QuadPart)/(double)ticksPerSecond.QuadPart;/*il
tempo impiegato da malloc e' dato dalla differenza tra il tempo finale e quello inziale*/

matrice_A(a,m,p,r,f);
matrice_B(b,p,n,r,f);
matrice_C(a,b,c,m,n,p);
printf("\n\nL'allocazione con calloc richiede %g secondi\n",elapsed_Time1);
printf("L'allocazione con malloc richiede %g secondi\n",elapsed_Time2);
if(elapsed_Time1==elapsed_Time2)/*se i due tempi coincidono...*/
{
printf("\nI tempi di esecuzione tra malloc e calloc non cambiano");
}
else
{
printf("\nI tempi di esecuzione tra malloc e calloc cambiano");
}

void matrice_A(int *a, int m, int p, int r, int f)


{
int i,j;
/*costruiamo la matrice A inserendoci i numeri random*/
for(i=0;i<m;i++)
{
for(j=0;j<p;j++)
{
*(a+i*p+j)=f+rand()%(r-f);/*generiamo numeri casuali nell'intervallo [f;r],
allocando dinamicamente per colonne*/
}

}
/*stampiamo la matrice A con i due cicli for annidati*/
printf("\n\n***MATRICE A[M][P] generata %d-righe e %d-colonne***\n\n",m,p);
printf("\t\t");
for(i=0;i<m;i++)
{

for(j=0;j<p;j++)
{
printf("%d ",*(a+i*p+j));
}
printf("\n\t\t");
}
}
- 37 -

void matrice_B(int *b, int p, int n, int r, int f)


{
int i,j;
/*costruiamoci la matrice B inserendoci i numeri casuali*/
printf("\n**************************************************");
for(i=0;i<p;i++)
{
for(j=0;j<n;j++)
{
*(b+i*n+j)=f+rand()%(f-r);/*generiamo numeri casuali nell'intervallo [f;r],
allocando dinamicamente per colonne*/
}

}
/*stampiamo la matrice B con i due cicli for nidificati*/
printf("\n***MATRICE B[P][N] generata %d-righe e %d-colonne***\n\n",p,n);
printf("\t\t");
for(i=0;i<p;i++)
{

for(j=0;j<n;j++)
{
printf("%d ",*(b+i*n+j));
}
printf("\n\t\t");
}
}

void matrice_C(int *a, int *b, int *c, int m, int n, int p)
{
int i,j,l;/*contatori*/
/*inizializziamo a 0 le componenti della matrice finale C per evitare la presenza di numeri
sporchi*/
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
*(c+i*n+j)=0;
}
}
printf("\n**************************************************");
/*Adesso facciamo il prodotto righexcolonne delle due matrici A e B,
cosi' da ottenere la matrice finale C*/
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
for(l=0;l<p;l++)
{
*(c+i*n+j)=*(c+i*n+j)+*(a+i*p+l)*(*(b+l*n+j));
}
}
}

/*stampiamo la matrice finale C[M][N]*/


printf("\n***MATRICE FINALE C[M][N] generata %d-righe e %d-colonne***\n\n",m,n);
printf("\t");
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
printf("\t%d ",*(c+i*n+j));
}
printf("\n\t");
}
printf("\n*********************************************************");

}
- 38 -

Screenshot esercizio 30

/*ESERCIZIO 33:
Scrivere una function C che legga, mediante una variabile “buffer” di 200char,
un file testo e lo visualizzi sullo schermo 40 char per riga e 25 righe per ogni schermata,
fermandosi finché non viene premuto un tasto per continuare.*/
/*Ricordiamo che per lettura bufferizzata si intende che i caratteri contenuti
nel file di testo sono letti, in questo caso viene letto un carattere per volta.
Mettiamo a fuoco queste cose. Dobbiamo avere:
1)40 caratteri per riga
2)1 schermata corrisponde a 25 righe */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFSIZE 200/*Size buffer dichiarato come costante globale per la lettura dei caratteri*/
void main()
{
int i_buf; /*indice del buffer*/
int c_riga=0; /*contatore di caratteri per riga, inizializzato a zero*/
int c_schermata=0; /*contatore di righe per schermata, inizializzato a zero*/
int i,j;/*il contatore i lo uso come indice del buffer nella stampa,
il contatore j lo uso per visualizzare l'indice delle righe su schermo*/
int r=1,s=1;/*r mi indica la riga, s la schermata*/
char ch;/*mi serve per leggere i singoli caratteri del testo*/
char buffer[BUFSIZE];/*questo e' l'array che mi contiene i caratteri del testo nel file*/
char nomefile[20]; /*nomefile e' l'array che conterra' il nome del file*/

FILE *fp; /*fp è un puntatore ad una struttura contenente informazioni sul file.
Serve per indirizzare tutte le operazioni sul file*/
printf("\nRicorda che il file da leggere si chiama 'Dante'(il file e' di tipo txt)\n");
printf("Inserire nome del file da leggere: ");
scanf("%s",&nomefile);
strcat(nomefile,".txt");/*concatena al nome del file immesso l'estensione txt blocco note
in modo tale che il file possa essere letto con un editor di testo
riconosciuto in automatico dal sistema operativo*/
printf("nomefile: %s\n", nomefile);
/*se il file non e' accessibile, il programma si arresta*/
if ((fp=fopen(nomefile,"r")) == NULL)
{
puts("Errore apertura file");
exit(1);
}
- 39 -

printf("\n\nSchermata %d\n",s);
/*Il ciclo while viene ripetuto finche' non si giunge alla fine del file*/
while (!feof(fp))
{
i_buf=0;/*azzeriamo inizialmente l'indice del buffer*/
/*finche' l'indice buffer e' minore del massimo size(cioe' finche' il buffer non e' pieno)
e finche' non siamo arrivati alla fine del file...*/
while(i_buf<BUFSIZE && !feof(fp))
{
ch=(char)getc(fp);/*Assegna a ch il carattere del file testo puntato da fp*/
buffer[i_buf] = ch;/*memorizziamo nel buffer il carattere letto*/
i_buf++;/*incrementiamo l'indice del buffer*/
}

/*Incrementa la variabile che tiene conto dei caratteri stampati per schermata*/
c_schermata = c_schermata + i_buf;

i=0;

while(i<i_buf)/*Mentre ci sono ancora elementi nel buffer da stampare*/


{
printf("%Riga corrente: %d\t",r);
/*Mentre non si e' stampati 40 caratteri sulla stessa riga e non sono finiti gli
elementi del buffer */
while(c_riga<40 && i<i_buf)
{
printf("%c",buffer[i]);/*stampiamo i 40 caratteri*/
c_riga++;/*incrementiamo il contatore delle righe*/
i++;
}
c_riga=0;
r++;/*incrementiamo il contatore delle righe da stampare*/
printf("\n");/*Vai alla riga a capo per memorizzare i successivi 40 caratteri*/
}
/*Dopo la stampa del buffer, se sei arrivato alla fine della schermata (hai raggiunto
cioe 25x40 = 1000 caratteri, posizionati sulla prossima schermata (lascia lo spazio) */
if(c_schermata == 1000)
{
s=s+1;/*incrementiamo i valore della schermata dopo le 25 righe*/
printf("\n");
system("pause");/*Ci fermiamo alla schermata corrente fino a che non viene
premuto un tasto per continuare*/
printf("\n\nSchermata corrente: %d\n",s);
c_schermata=0;/*azzera il contatore dei caratteri per schermata*/
r=1;/*arrivati alla nuova schermata riprendiamo a contare le righe da 1*/
}
}
fclose(fp); /*Chiusura del file*/
printf("\n");
}
Screenshot esercizio 33
- 40 -

/*ESERCIZIO 36:
Simulare in C la gestione di una pila (stack) tramite array statico (può essere anche un array di
struct) creando le funzioni di manipolazione push() [inserimento] e pop() [eliminazione].
Il programma deve prevedere un menù che consenta di scegliere l’operazione da eseguire.
__
a | |__| n -->TESTA DELLO STACK: contiene l'indice nell'array dell'ultima
r | |__| informazione inserita nello stack: e' qui che avvengono le
r | |__| fasi di push e pop
a | |__|
y | |__| 1 */
/*Annotazioni essenziali sulla struttura della pila:
La PILA detta anche stack, è una struttura lineare aperta in cui l'accesso alle componenti
per l'inserimento e l'eliminazione avvengono solo ad un estremo della struttura(detta testa della
pila).
La pila è una struttura LIFO(LAST INPUT FIRST OUTPUT), perché l'ultimo elemento inserito è
il primo ad essere eliminato.Simuliamo lo stack con un array statico, in quanto esso puo
essere immaginato appunto come un array rovesciato: l'inserimento e l'eliminazione che avvengono
alla testa dello stack si verificano sull'ultima componente usata dall'array.
*/

#include<stdio.h>
#define MAX_STACK_SIZE 100/*il nostro stack puo' contenere massimo 100 elementi*/
void push_s(char elem,char p_stack[], short *head );
void pop_s(char *elem, char p_stack[], short *head);
void main()
{
int scelta;/*questa variabile ci consente di fare la scelta nel menu'*/
int i;/*indice*/
char elem;/*l'elemento di tipo char che vogliamo inserire/prelevare nello stack*/
char stack[MAX_STACK_SIZE];/*l'array che mi contiene gli elementi della pila*/
short head=-1;/*la testa dello stack inizializzata a -1 mi indica stack vuoto*/
elem=(char)(65);/*il primo elemento da inserire e' la lettera A maiuscolo
che nel codice ASCII vale 65*/
printf("Che cosa vuoi fare?\nMetti sotto alle possibili scelte la tua scelta: \n");
do{
printf("\n[1]Inserisci un elemento con push");
printf("\n[2]Elimina un elemento con pop");
printf("\n[3]Esci dal programma");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");/*elimina l'inutile dallo schermo generato dal continuo ciclo del while*/
switch(scelta)
{
case 1: /*Se la pila si riempie del tutto*/
if(head==MAX_STACK_SIZE-1)
{
printf("\nPila piena, impossibile inserire\n");
}
else
{
push_s(elem,stack,&head);
/*la testa aggiunge un elemento allo stack:
questo elemento viene inserito nella prima componente del
nostro vettore e, ad ulteriori inserimenti, l'elemento
viene inserito nelle successive componenti del vettore*/
for(i=head;i>=0;i--)
{
printf("a[%d]=%c\n",i,stack[i]);
}
elem++;/*incremento il valore di elem cosi' da avere 66,67,68, ecc
che nel codice ASCII valgono rispettivamente B,C,D, ecc*/
}
break;
- 41 -

case 2:
pop_s(&elem,stack,&head);
/*Se la pila e' vuota non e' possibile eliminare*/
if(head==-1)
{
printf("\nPila vuota, impossibile eliminare\n");
}
else
{
/*stampami gli elementi che rimangono e che
non sono stati ancora eliminati*/
for(i=head;i>=0;i--)
printf("a[%hd]=%c\n",i,stack[i]);
}
break;
case 3:
printf("\nArrivederci");
break;

}
}while(scelta!=3);/*finche' non premo 3 non esco dal programma*/

void push_s(char elem, char p_stack[], short *head)/* *head e' il puntatore alla testa della pila,
cioe' all'estremo dove vegono effettuate le operazioni di push e pop*/
{
if(*head<MAX_STACK_SIZE-1)/*perche' ricordiamo che partiamo da 0
per arrivare a 99, ottenendo il max_size=100*/
*(p_stack+ ++*head)=elem;/*il puntatore alla testa viene prima incrementato per poi essere
utilizzato per stabilire la posizione della componente in cui andare ad inserire il valore
dell'elemento elem*/
}
void pop_s(char *elem, char p_stack[], short *head)
{
if(*head!=-1)/*si entra nell'if solo se lo stack non e' vuoto*/
*elem=*(p_stack+(*head)--);/*il valore di head viene prima utilizzato per prelevare
l'informazione e restituirla nella variabile puntata da elem, quindi ritornarla come parametro di
uscita della function e successivamente viene decrementato.Quest' ordine temporale delle operazioni e'
realizzato mediante gli operatori di pre-incremento e post-decremento*/
}
Screenshot esercizio 36
Inserimento Eliminazione
- 42 -

/*ESERCIZIO 37:
Simulare in C la gestione di una coda (queue) tramite array statico (può essere anche un array di
struct) creando le funzioni di manipolazione enqueue() [inserimento] e dequeue() [eliminazione].
Il programma deve prevedere un menù che consenta di scegliere l’operazione da eseguire.
Le informazioni NON vanno spostate!

a | |___| n
r | |___| -->FONDO: contiene l'indice nell'array della
r | |_C_| prima componente libera(dove inserire la prossima
a | |_B_| informazione
y | |_A_| 1--->TESTA: contiene l'indice nell'array dell'informazione
di testa della coda(la prossima da eliminare)*/
/*Annotazioni utili sulla coda:
La coda è una struttura lineare aperta in cui l'accesso alle componenti avviene solo ai due estremi:
-l'eliminazione avviene solo all'inizio della struttura(testa).
-l'inserimento avviene solo alla fine(fondo).
La coda si presenta come una struttura FIFO( FIRST INPUT FIRST OUTPUT) perché il primo elemento
inserito è il primo ad essere eliminato.
La fase di eliminazione si chiama dequeue e la fase di inserimento enqueue.
A differenza dello stack, la coda tende a slittare verso la fine dell'array in quanto
l'inserimento e l'eliminazione avvengono in due estremi opposti: quindi arriveremo prima
o poi all'overflow di array.
Nel caso della coda, tutte le componenti dell'array che si liberano in seguito ad eliminazione di
informazioni, diventano spazio inutilizzabile e quindi la coda più facilmente della pila tende a
raggiungere il massimo riempimento. */

#include <stdio.h>
#define MAX 10/*la coda puo' contenere massimo 10 elementi*/
void enqueue(char *elem,char p_coda[],int *bottom);
void dequeue(int *head);
void main()
{
char coda[MAX]; //stack di MAX 10 elementi
int scelta;
int head=0,bottom=0;//head mi rappresenta la testa della coda, bottom invece e' il fondo
//head e fondo sono inizializzate a 0 perche’ sono vuote
char elem=(char)65;/*l'elemento di tipo char che vogliamo inserire/prelevare nello stack:
il primo elemento da inserire e' la lettera A maiuscoloche nel codice ASCII vale 65*/
int i;
do
{
printf("\nMENU' CODA:\n");
printf("[1]Inserisci un elemento nel FONDO con enqueue\n");
printf("[2]Elimina un elemento nella TESTA con dequeue\n");
printf("[3]Esci dal programma");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
{
case 1:
if(bottom<MAX)//Se non andiamo oltre il massimo size della coda
{
enqueue(&elem,coda,&bottom);
/*la visualizzazione della coda parte dal fondo dove inserisco
fino ad arrivare alla testa dove prelevo*/
for(i=bottom;i>=head;i--)
{
printf("a[%d]=%c\n",i,coda[i]);
}
elem++;/*incremento il valore di elem cosi' da avere 66,67,68, ecc
che nel codice ASCII valgono rispettivamente B,C,D, ecc*/
bottom++;//incremento il fondo ogni volta che inserisco un elemento
break;
}
else
{
printf("\nCoda piena, impossibile inserire\n");
break;
}
- 43 -

case 2:
if(bottom!=0)//se il fondo non e' vuoto
{
dequeue(&head);
/*la visualizzazione della coda parte dal fondo dove inserisco
fino ad arrivare alla testa dove prelevo*/
for(i=bottom-1;i>=head;i--)
{
printf("a[%d]=%c\n",i,coda[i]);
}
break;
}
else
{
printf("\nNon c'e' nessun elemento nel fondo. Impossibile eliminare");
break;
}

case 3:printf("\nArrivederci");
break;
}
}while(scelta!=3);
}

void enqueue(char *elem,char p_coda[],int *bottom)


{
*(p_coda + (*bottom)) = *elem;//inserisco un elemento nel fondo visto che la coda
//prevede che gli elementi vadano inseriti dal fondo
}

void dequeue(int *head)


{
(*head)++;/*aumento l'indice head che punta alla testa dello stack
in modo che prelevando dalla testa non visualizzo piu' l'indice
e l'elemento della testa che adesso e' stato prelevato.
Ovviamente quando prelevo, l'indice dell'elemento prelevato
non e' piu' utilizzabile: questa e' una caratteristica della coda*/
}

Screenshot esercizio 37
Inserimento Eliminazione
- 44 -

/*ESERCIZIO 39
Simulare in C l’algoritmo di visita di una lista lineare già memorizzata mediante un array
statico di struct (come nella tabella in basso) in cui il primo campo contiene l’informazione
ed il secondo contiene il link al nodo successivo (in questo caso il link è l’indice di una
componente dell’array). Memorizzando nell’array i dati come mostrato nella figura che segue,
l’output del programma consiste nell’elenco di nomi ordinato alfabeticamente.

Dati di input: ListaNomi


________________________
|_____INFO____|__pnext__|
|____Anna_____|____5____|
|____Mario____|____8____|
|__Giuseppe___|____6____|
|____Angela___|____0____|<---p_TESTA
|___Valeria___|___-1____|
|___Fabrizio__|____7____|
|__Marianna___|____1____|
|__Giovanni___|____2____|
|____Patrizia_|____10___|
|___Valentina_|____4____|
|_____Sara____|____9____| */

#include<stdio.h>
#include<string.h>
/*creazione della struct globale, da poter utilizzare all'interno sia del main che della function,
che rappresenta un nodo della lista che conterra' i nostri 11 elementi*/
struct elenco
{
char nome[11];/*questo campo contiene l'informazione cioe' il nome della persona*/
short pnext;/*questo campo contiene i link al prossimo nodo cioe' all'informazione successiva*/
};

typedef struct elenco ELENCO;/*definiamo piu' chiaramente la struttura elenco con typedef*/
/*definiamo con un array statico di struct l'elenco non ordinato con campo nome e pnext,
dove pnext contiene il puntatore all'elemento successivo in ordine alfabetico*/
ELENCO persone[11] = { {"Anna",5},
{"Mario",8},
{"Giuseppe", 6},
{"Angela",0},
{"Valeria",-1},
{"Fabrizio",7},
{"Marianna",1},
{"Giovanni",2},
{"Patrizia",10},
{"Valentina",4},
{"Sara",9}};

void ordina_lista(ELENCO persone []);


void main()
{
int i;
printf("\nElenco di persone NON ORDINATO");
for(i=0;i<11;i++)
{
printf("\n[%d-nome]\t%s \tp_next=%d\n",i,persone[i].nome,persone[i].pnext);
}
ordina_lista(persone);
}
- 45 -

void ordina_lista(ELENCO persone[])


{
int i=0;//il contatore della lista inizializzato a 0
int head=3;//il primo elemento della lista cioe' quello contenuto nella testa ha link=3
int *punt=&head;//questa variabile puntatore punta alla testa e, partendo da essa scorre la
lista arrivando fino alla fine nel ciclo while
int bottom=-1;//la fine della lista ha link -1
printf("\n\nElenco di persone ORDINATO");
/*Il while andra' avanti con la stampa della lista fino a che punt non arriva alla fine della
lista cioe' e' uguale a 1. Quindi finche' non ci troviamo sull'ultimo nodo, saranno visualizzati i
campi nome e i campi p_next del nodo puntato da punt e dopodiche' la variabile punt viene definita come
campo p_next del nodo puntato da punt. Si realizza l'avanzamento da un nodo al successivo*/
while(*punt != bottom)
{
/*stampa l'informazione del nodo corrente*/
printf("\n[%d-nome]\t%s \tp_next=%d\n",i,persone[*punt].nome,persone[*punt].pnext);
*punt = persone[*punt].pnext; /*assegna a punt il link (indice) del nodo successivo
contenuto nel campo pnext del nodo corrente*/
i++; /*incremento della variabile contatore*/
}
}
Screenshot esercizio 39
- 46 -

/*ESERCIZIO 41
Realizzare la gestione di una lista lineare mediante menù (visualizzazione mediante visita,
inserimento in testa, inserimento in mezzo, eliminazione in testa, eliminazione in mezzo)
implementando la lista lineare con una struttura autoriferente dinamica.*/
/*Cose fondamentali da ricordare:
-INSERIMENTO IN MEZZO
Per inserire un elemento dopo quello corrente, bisogna:
1)inserire il link che va dall'elemento nuovo al successore
2)modificare il link che va dall'elemento nuovo al successore
-INSERIMENTO IN TESTA
Ricordiamo che per inserire in testa bisogna:
1)inserire il link che va dall'elemento nuovo alla testa della struttura
2)modificare il link alla testa affinche' punti al nuovo elemento
-ELIMINAZIONE IN MEZZO
Per eliminare in mezzo bisogna modificare il link del predecessore affinche' punti al successore
-ELIMINAZIONE IN TESTA
Ricordiamo che per eliminare in testa bisogna eliminare l'elemento in testa, cioe' bisogna modificare
il link che punta all'inizio della
struttura*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*stabiliamo la struttura che mi contiene le informazioni di ogni nodo: queste sono nome ed eta'*/
typedef struct
{
char nome[20];
int eta;
}INFO_FIELD;

/*stabiliamo la struttura del singolo nodo*/


struct PERSONA
{
INFO_FIELD info;/*un campo informazione che si chiama info ed e' di tipo
INFO_FIELD dichiarato sopra con una typedef*/
struct PERSONA *p_next;/*p_next e' un puntatore autoriferente perche' all'interno della
struttura struct PERSONA c'e' un puntatore allo stesso tipo della struttura, quindi un
puntatore alla struct persona*/
}*head,*punt;/*head e' il puntatore di testa e punt e' il puntatore di lavoro al nodo corrente*/

struct PERSONA *crea_lista();


void elimina_in_mezzo(struct PERSONA *p_punt);
void inserisci_in_testa(INFO_FIELD dato, struct PERSONA **p_head);
void inserisci_in_mezzo(INFO_FIELD dato, struct PERSONA *punt);
void elimina_in_testa(struct PERSONA **p_head);
void cerca_nodo(struct PERSONA **p_punt,char key[]);
void main()
{
INFO_FIELD nuovo_dato;/*variabile struttura per l'inserimento di un nuovo nodo*/
head=crea_lista();/*inizializziamo la lista rendendola vuota*/
int scelta;
int num_elem=0;/*variabile contatore che mi indica la posizione degli elementi all'interno
della lista*/
int i=0;//indice che si incrementa quando viene inserito un nuovo nodo e si decrementa quando
viene eliminato un nodo*/
char testo[100];//questa variabile mi serve come buffer di stringa
nell'inserimento/eliminazione in mezzo
do
{

printf("\n\nMENU' LISTA\n");
printf("\n[1]Visualizza la lista");
printf("\n[2]Inserisci un elemento in testa");
printf("\n[3]Inserisci un elemento in mezzo(dopo il nodo corrente)");
printf("\n[4]Elimina un elemento in testa");
printf("\n[5]Elimina un elemento in mezzo(dopo il nodo corrente)");
printf("\n[6]Esci dal programma");
printf("\n\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
- 47 -

{
case 1:
punt=head;/*il puntatore punt punta alla testa quindi punta al primo nodo e
parte da qui per visualizzare l'intera lista e per permettere le varie
operazioni*/
if(head==NULL)/*se la testa e' vuota*/
{
printf("\nLa lista e' vuota\n");
break;
}
else
{

while(punt!=NULL)/*finche' non siamo arrivati alla fine della lista*/


{
num_elem++;
/*stampa l'informazione del nodo corrente*/
printf("\n[%d elemento sulla lista] nome:%s eta':%d
p_next:%d",num_elem,punt->info.nome,punt->info.eta,punt->p_next);
punt=punt->p_next;/*assegna a punt il link (indice) del nodo s
uccessivo contenuto nel campo pnext del
nodo corrente*/
}
num_elem=0;//azzeriamo il valore dell'indice in modo che alla prossima
visita ripartiamo dal giusto indice
break;
}

case 2:
printf("\nInserisci il nome: ");
scanf("%s",&nuovo_dato.nome);
printf("\nInserisci l'eta': ");
scanf("%d",&nuovo_dato.eta);
inserisci_in_testa(nuovo_dato,&head);
i++;//incrementiamo perche' abbiamo inserito
break;

case 3:
if(i<1)/*se non ho inserito almeno un elemento non posso inserire nel mezzo*/
{
printf("\nNon puoi inserire finche' non immetti almeno un elemento in
testa alla lista\n");
break;
}
else
{
printf("\nInserisci il nome: ");
scanf("%s",&nuovo_dato.nome);
printf("\nInserisci l'eta': ");
scanf("%d",&nuovo_dato.eta);
printf("\nDopo quale nodo corrente vuoi inserire il nuovo
dato?\n(inserisci il nome)Il dato va inserito dopo questo nodo: ");
fflush(stdin);
scanf("%s",testo);
punt = head;//la variabile punt punta alla testa della lista
cerca_nodo(&punt,testo);//chiamata per verificare la presenza/non
presenza del dato che deve essere il nodo corrente
if(!punt)//se non ho trovato l'informazione corrente dopo la quale
immettere il nuovo dato
{
printf("\nIl nodo corrente da cercare non e' stato trovato");
break;
}
inserisci_in_mezzo(nuovo_dato,punt);
printf("\nINSERIMENTO AVVENUTO CON SUCCESSO!");
i++;//incrementiamo perche' abbiamo inserito
break;
}

case 4:
- 48 -

if(head==NULL)/*se la testa e' vuota*/


{
printf("\nImpossibilire eliminare, la testa e' vuota\n");
break;
}
else
{
elimina_in_testa(&head);
i--;//decrementiamo perche' abbiamo prelevato
}
break;
case 5:if(i<2)/*se non ho almeno due elementi non posso eliminare nel mezzo*/
{
printf("\nNon puoi eliminare, devi inserire minimo due elementi\n");
break;
}
else
{
printf("\nDopo quale nodo corrente vuoi eliminare il dato?\n(inserisci
il nome)Il dato si elimina dopo questo corrente: ");
fflush(stdin);
scanf("%s",testo);
punt = head;
cerca_nodo(&punt,testo);//chiamata per verificare la presenza/non
presenza del dato che deve essere il nodo corrente
if(!punt)//se non ho trovato l'informazione corrente dopo la quale
immettere il nuovo dato
{
printf("\nIl nodo corrente da cercare non e' stato trovato");
break;
}
elimina_in_mezzo(punt);
printf("\nELIMINAZIONE AVVENUTA CON SUCCESSO!");
i--;//decrementiamo perche' abbiamo prelevato
break;
}

case 6:printf("\nArrivederci");
break;
}
}while(scelta!=6);
}

/*il parametro dato e' l'informazione da inserire


mentre il secondo parametro punta al nodo corrente dopo il quale sara' inserito il nuovo nodo*/
void inserisci_in_mezzo(INFO_FIELD dato, struct PERSONA *punt)
{
struct PERSONA *ptr;/*ptr e' un puntatore al tipo nodo della lista*/
ptr=calloc(1,sizeof(struct PERSONA));/*ptr viene definito con la calloc che chiede di allocare
un blocco di memoria per il nuovo nodo*/
ptr->info=dato;/*copia il dato da inserire nel campo informazione del nuovo nodo*/
ptr->p_next=punt->p_next;/*si definisce il campo p_next del nuovo nodo uguale al valore
del campo p_next del nodo puntato da punt: in questo modo il nuovo nodo
viene collegato con il resto della nuova lista*/
punt->p_next=ptr;/*rimane da collegare la porzione di lista che precede questo nuovo nodo con
il nuovo nodo: per fare questo viene definito il campo p_next del nodo puntato da punt con il
valore ptr*/
punt=ptr;/*aggiorna punt a nodo corrente*/
}
- 49 -

/*dato rappresenta l'informazione che noi desideriamo copiare nel primo nodo della lista e **p_head
rappresenta il puntatore alla testa della lista. head e' di per se un puntatore ma in questo caso
l'inserimento in testa coinvolge la modifica di questo puntatore e quindi non e' solo un puntatore
ma anche una variabile di uscita perche' Il C prevede che i parametri di uscita di una function debbano
essere passati per riferimento, allora e'necessario dichiarare il parametro formale che sia di uscita e
che sia unpuntatore con il doppio asterisco perche' un asterisco e' perche' e' un parametro di uscita e
l'altro asterisco e' perche' e' un puntatore*/
void inserisci_in_testa(INFO_FIELD dato, struct PERSONA **p_head)
{
struct PERSONA *ptr;/*ptr mi serve a definire la posizione del primo nodo nella lista*/
ptr=calloc(1,sizeof(struct PERSONA));/*alloca un blocco di memoria di ampiezza pari alla
struttura di un nodo*/
ptr->info=dato;/*definisce il campo informazione*/
ptr->p_next=*p_head;/*copia nel campo pnext(quindi nel nodo successivo) l'indirizzo di memoria
di quello che prima era il primo nodo e quindi era puntato dalla variabile p_head*/
*p_head=ptr;/*aggiorna head al nuovo nodo*/
}

void elimina_in_testa(struct PERSONA **p_head)


{
*p_head=(*p_head)->p_next;/*head punterà al nodo successivo e la vecchia testa verrà saltata*/
}

void elimina_in_mezzo(struct PERSONA *punt)/*lo scopo di questa funzione e' l'eliminazione


del nodo che segue il nodo puntato dalla variabile p_punt, unico parametro della funzione*/
{
struct PERSONA *ptr; /*puntatore alla struttura di appoggio: quindi ptr e' il puntatore di
lavoro che si sposta sulla lista*/
ptr = punt->p_next;/*associa a ptr il campo p_next del nodo puntato dalla variabile p_punt,
cioe' ptr contiene l'indirizzo di memoria del successore del nodo puntato
da p_punt*/
punt->p_next= ptr->p_next;/* ptr diventa il successivo di punt così quest'ultimo non punterà
piu' al successore: il campo p_next del nodo puntato da p_punt viene definito uguale al
campo p_next del nodo puntato da ptr, cioe' il nodo puntato da p_punt ora non punta piu' al suo vecchio
successore ma punta al successore del successore,cioe' salta un nodo*/
free(ptr); /*libera la memoria del nodo puntato da p_punt che inizialmente salvato in ptr*/
}

/*questa funzione ci aiuta a trovare il nodo corrente all'interno della lista


dopo del quale va o inserito o eliminato un elemento dalla scelta dell'utente*/
void cerca_nodo(struct PERSONA **p_punt,char key[])
{
while(*p_punt!=NULL)
{
if(strcmp((*p_punt)->info.nome,key)==0)
{
return;//ritorna al programma chiamante il puntatore del nodo cercato
}
*p_punt = (*p_punt)->p_next; //continua la ricerca passando al nodo successivo
}

*p_punt=NULL;//l'ultimo nodo a cui punt punta e' vuoto/nullo


return;//se il dato non e' stato trovato non fare nulla perche' siamo usciti dal ciclo
}

struct PERSONA *crea_lista()/*inizializziamo la lista, svuotandola*/

{
struct PERSONA *head;
head=NULL;
return head;
}
Screenshot esercizio 41
Screenshot inserimento in testa Screenshot inserimento nel mezzo

Screenshot eliminazione in testa Screenshot eliminazione nel mezzo


- 50 -

/*ESERCIZIO 44
Realizzare in C le funzioni per la gestione, mediante menù, delle strutture dati pila e coda
mediante lista lineare dinamica e generica con [rispettivamente senza] nodo sentinella.
In questo caso FACCIAMO LISTA LINEARE GENERICA SENZA NODO SENTINELLA */
/*Ricordiamo che il nodo sentinella serve per evitare di avere due function per l'inserimento e due
function per l'eliminazione. Infatti è possibile avere un'unica sequenza di operazioni sia per gli
inserimenti/eliminazioni sulla testa che per quelli fatti sul nodo corrente.
Occorre semplicemente aggiugere un nodo fittizio che funga da testa ma che punti alla testa
reale della lista: un nodo sentinella.
Ogni modifica sulla testa comporterà in questo modo alla sola alterazione del campo puntatore del
nodo sentinella*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*stabiliamo la struttura che mi contiene le informazioni: l'informazione e' il campo nome*/
typedef struct
{
char nome[20];
} INFO_FIELD;

void *crea_lista();//crea lista vuota


void visita(void *);//function che mi fa la visita della lista
void insl_testa(short len_info, INFO_FIELD *p_dato, void **p_head);//inserimento in testa
void insl_nodo(short len_info, INFO_FIELD *p_dato, void **p_punt);//inserimento in mezzo
void elim_testa(void **p_head);//elimina in testa
void main()
{
/* dichiarazione della lista (nel main perchè usiamo delle function standardizzate) */
/*stabiliamo la struttura del singolo nodo*/
struct PERSONA
{
INFO_FIELD info;/*un campo informazione che si chiama info ed e' di tipo
INFO_FIELD dichiarato sopra con una typedef*/
struct persona *p_next;/*p_next e' un puntatore autoriferente perche' all'interno della
struttura struct PERSONA c'e' un puntatore allo stesso tipo della struttura, quindi un
puntatore alla struct persona*/
};
int scelta;
short len_info;//lunghezza informazione
struct PERSONA *head, *punt, *bottom;/*head e' il puntatore di testa, bottom e' il puntatore di
fondo e punt e' il puntatore di lavoro al nodo corrente*/

INFO_FIELD nuovodato;/*creiamo questa variabile struttura che ci serve per l'inserimento di un


nuovo nodo*/
len_info=sizeof(INFO_FIELD);

head = crea_lista();/*inizializziamo la lista rendendola vuota cioe' la testa della lista non
contiene nessun elemento*/

puts("\nPILA E CODA MEDIANTE LISTA LINEARE GENERICA\n");


do
{
printf("PREMERE \n0 PER USCIRE DAL PROGRAMMA\n1 PER LA GESTIONE DELLA PILA \n2 PER LA
GESTIONE DELLA CODA\n");
fflush(stdin);
printf("\nSCELTA: ");
scanf("%d", &scelta);
switch (scelta)
{
case 1:
{
system("cls");
printf("\n\t MENU' PILA :\n0 per l'uscita dal programma\n1 per la visualizzazione
della pila \n2 per l'inserimento nella pila push()""\n3 per l'eliminazione dalla pila
pop()");
do
{
printf("\nscelta: ");
fflush(stdin);
scanf("%d", &scelta);
- 51 -

switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;

case 1:
if (head == NULL)//se la testa e' vuota
printf("\nPILA VUOTA\n");
else
{
punt = head; //per la visualizzazione
printf("\nVisita della pila\n\n");
visita((void *)punt);
}
break;

case 2:
printf("\nInserisci il nome: ");
fflush(stdin);
gets(nuovodato.nome);
insl_testa(len_info, &nuovodato, (void **)&head);
printf("Inserimento nella pila effettuato\n");
break;

case 3:
if (head == NULL)//se la testa e' vuota
printf("\nPILA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
else
{
printf("Eliminazione in testa...\n");
elim_testa((void **)&head);
}
break;
}
}while (scelta != 0);
break;
}

case 2:
{
system("cls");
do
{

printf("\n\t MENU CODA:\n0 per uscire dal programma \n1 per la visualizzazione della
coda \n2 per l'inserimento nella coda enqueue()""\n3 per l'eliminazione dalla coda
dequeue()\n");
printf("\nscelta: ");
fflush(stdin);
scanf("%d", &scelta);
switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;

case 1:
if (head == NULL)//se la testa e' vuota
printf("\nCODA VUOTA\n");
else
{
punt = head; //per la visualizzazione
printf("\nVisita della coda\n\n");
visita((void *)punt);
}
break;

case 2:
if (head == NULL)//se la testa e' vuota
- 52 -

{
printf("Inserisci il nome: ");
fflush(stdin);
gets(nuovodato.nome);
insl_testa(len_info, &nuovodato, (void **)&head);
bottom = head;/*con l'inserimento in testa, il fondo==testa
printf("Inserimento nella coda effettuato\n");
break;
}
else
{
if (bottom != NULL)//se il fondo non e' vuoto
{
printf("Inserisci il nome: ");
fflush(stdin);
gets(nuovodato.nome);
insl_nodo(len_info, &nuovodato, (void **)&bottom);
printf("Dato inserito in coda!\n");
break;
}

}
case 3:
if (head == NULL)//se la testa e' vuota
printf("\nCODA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
else
{
printf("Eliminazione in testa...\n");
elim_testa((void **)&head);
}
break;
}
}while (scelta != 0);
break;
}

case 0:
printf("\nFINE PROGRAMMA\n");
break;
}
} while (scelta != 0);

}
/*inizializzo la lista svuotandola*/
void *crea_lista()
{
char *testa;
testa = NULL;
return testa;
};
- 53 -

//inserisce il dato in testa alla lista


/*dato rappresenta l'informazione che noi desideriamo copiare nel primo nodo della lista e **p_head
rappresenta il puntatore alla testa della lista. head e' di per se un puntatore ma in questo caso
l'inserimento in testa coinvolge la modifica di questo puntatore e quindi non e' solo un puntatore
ma anche una variabile di uscita perche' Il C prevede che i parametri di uscita di una function debbano
essere passati per riferimento, allora e'necessario dichiarare il parametro formale che sia di uscita e
che sia un puntatore con il doppio asterisco perche' un asterisco e' perche' e' un parametro di uscita
e l'altro asterisco e' perche' e' un puntatore*/
void insl_testa(short len_info, INFO_FIELD *p_dato, void **p_head)
{
struct lista/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = calloc(1, sizeof(struct lista));//alloca 1 blocco di memoria della lunghezza della struct
inizializzando ptr a 0
memcpy(ptr, p_dato, len_info);/*copia len_info byte da *p_dato a *ptr senza interessarsi della
struttura delle informazioni*/
ptr->p_next = (struct lista *)*p_head;/*facciamo puntare il nuovo nodo alla testa della
struttura: il cast (struct lista *) e' obbligatorio prima di usare un puntatore generico, ma
non ci vuole quando lo si definisce*/
*p_head = ptr; //aggiorna head al nuovo nodo
}
}

//inserisce dato dopo nodo corrente


/*il parametro dato e' l'informazione da inserire
mentre il terzo parametro p_punt punta al nodo dopo il quale sara' inserito il nuovo nodo:
d'altra parte p_punt e' preceduto dal doppio asterisco in quanto
e' un puntatore che risulta essere un parametro di uscita di questa function
perche' questa function, dopo aver inserito il nuovo nodo, fa avanzare il puntatore al nuovo nodo
e quindi modifica il suo valore*/
void insl_nodo(short len_info, INFO_FIELD *p_dato, void **p_punt)
{
/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = calloc(1, sizeof(struct lista));
if (ptr == NULL)
{
printf("\nAllocazione del nodo non riuscita\n");
}
else
{
memcpy(ptr, p_dato, len_info);//copia len_info byte da *p_dato a *ptr, senza
interessarsi della struttura

// aggancia il nodo nuovo al prossimo di p_punt


ptr->p_next = ((struct lista *)*p_punt)->p_next;

((struct lista *)*p_punt)->p_next = ptr; //il nodo p_punt e' agganciato a ptr

*p_punt = ptr; //aggiorna punt a nodo corrente


}
}
- 54 -

/*questa funzione ci consente di visualizzare gli elementi contenuti sia nella coda che nella pila,
attraverso il puntatore punt*/
void visita(void *punt)
{
/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
Struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = punt;
while (ptr->p_next != NULL)/*finche' non siamo arrivati alla fine della lista*/
{
printf("NOME = %s p_next= %d \n", (*ptr).info.nome, (*ptr).p_next);
ptr = ptr->p_next;/*assegna a punt il link (indice) del nodo successivo contenuto nel
campo pnext del nodo corrente*/
}
/*usciti dal while, ci troviamo sull'ultimo nodo e allora l'ultima printf visualizza i campi
dell'ultimo nodo*/
printf("NOME = %s p_next= %d \n", (*ptr).info.nome, (*ptr).p_next);
}

/*lo scopo di questa funzione e' l'eliminazione del nodo puntato da ptr e che vogliamo considerare*/
void elim_testa(void **p_head)
{
/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;

ptr = ((struct lista *)*p_head)->p_next;//facciamo puntare head al nodo successivo per tagliare
la vecchia testa*/

free((struct lista *)*p_head);/*dealloca la memoria puntata dal vecchio nodo*/

*p_head = ptr;//aggiorna head al nuovo nodo


}
Screenshot esercizio 44

PILA CODA
- 55 -

/*In questo caso GESTIAMO LA PILA CON LA LISTA DINAMICA(CON NODO SENTINELLA)*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*stabiliamo la struttura che mi contiene le informazioni di ogni nodo: abbiamo l'unico campo nome*/
typedef struct{
char nome[20];
} INFO_FIELD;

void *crea_lista();//crea lista con il nodo sentinella


void visita(void *p_punt);
void ins_nodo(short len_info, INFO_FIELD *p_dato, void *p_punt);//inserimento
void elim_nodo(void *p_punt);//eliminazione
void main()
{
/*stabiliamo la struttura del singolo nodo*/
struct persona
{
INFO_FIELD info;/*un campo informazione che si chiama info ed e' di tipo
INFO_FIELD dichiarato sopra con una typedef*/
Struct persona *p_next;/*p_next e' un puntatore autoriferente perche' all'interno della
struttura struct PERSONA c'e' un puntatore allo stesso tipo della struttura, quindi un
puntatore alla struct persona*/
};
typedef struct persona PERSONA;
int scelta;
PERSONA *head;/*head e' il puntatore di testa*/
short len_info;//lunghezza informazione

INFO_FIELD nuovodato;//campo informazioni che verrà inserito nel nuovo nodo da creare
len_info=sizeof(INFO_FIELD);

/*Il nodo sentinella viene inserito in testa come nodo vuoto.


Quando inserisco o elimino, lo farò da questo (funzionerà come ins_testa)
head, punta a questo nodo sentinella.*/
head = (PERSONA*)crea_lista();//verrà restituito un *void, quindi effettuiamo cast

puts("\nPILA MEDIANTE LISTA DINAMICA \n");


do
{

printf("\n0 per l'uscita dal programma\n1 per la visualizzazione della pila \n2 per
l'inserimento nella pila push()" "\n3 per l'eliminazione dalla pila pop()");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;

case 1:
/*Stampa se dopo il nodo sentinella c'è qualcosa.
Inizia a stampare dal nodo successivo sentinella*/
if(head->p_next)
{
visita((void *)(head)->p_next); //stampa la lista dopo ogni operazione
break;
}
else
{
printf("\nLa pila e' vuota!");
break;
}
- 56 -

case 2:
// Inserisce dopo nodo sentinella, senza utilizzare "insl_testa"
printf("\nInserisci il nome ");
fflush(stdin);
gets(nuovodato.nome);
ins_nodo(len_info, &nuovodato, (void *)head);
printf("\nInserimento nella pila effettuato\n");
break;

case 3:
//Elimina il nodo dopo il nodo sentinella, senza utilizzare "elim_testa"
if (head->p_next!= NULL)// Se dopo il nodo sentinella c'è qualcosa
{
printf("Eliminazione in testa...\n");
elim_nodo((void *)head);
}
else
{
printf("\nPILA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
}
break;
}
}while(scelta!=0);

/*Crea la lista e restituisce un puntatore iniziale della testa


che e' vuoto. La testa punterà al nodo sentinella */
void *crea_lista()
{
struct lista
{ INFO_FIELD info;
struct lista *p_next;
};
struct lista *testa;// il tipo non importa, nel main ho il cast
testa=(struct lista*)calloc(1,sizeof(struct lista));
testa->p_next=NULL;//anche se posso evitare questa istruzione visto che calloc inizializza a 0
return testa;
}

/*Date in input le informazioni da inserire, la loro lunghezza e il puntatore p_punt si inserisce in


testa, dopo il nodo sentinella, il nuovo nodo. Quindi il nuovo nodo punterà al nodo successivo di
p_punt ed il nodo p_punt punterà proprio al nuovo nodo*/
void ins_nodo(short len_info, INFO_FIELD *p_dato, void *p_punt)
{
/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr= calloc(1, sizeof(struct lista));//alloca 1 blocco di memoria della lunghezza della struct
inizializzando ptr a 0
memcpy(ptr, p_dato, len_info);/*copia len_info byte da *p_dato a *ptr senza interessarsi della
struttura delle informazioni*/

//aggancia il nuovo nodo al prossimo di p_punt


ptr->p_next = ((struct lista *)p_punt)->p_next;/*colleghiamo il nuovo nodo con la vecchia testa: il
cast (struct lista *) e' obbligatorio prima di usare un puntatore generico, ma non ci vuole quando lo
si definisce*/
((struct lista*)p_punt)->p_next= ptr; //aggiorna head al nuovo nodo

}
- 57 -

/*questa funzione ci consente di visualizzare gli elementi contenuti sia nella lista che nella pila,
attraverso il puntatore punt*/
void visita(void *p_punt)
{
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = ((struct lista*)p_punt);
while (ptr!= NULL)/*finche' non siamo arrivati alla fine della lista*/
{
printf("NOME = %s p_next= %d \n", (*ptr).info.nome,(*ptr).p_next);
ptr = ptr->p_next;/*assegna a punt il link (indice) del nodo successivo contenuto nel campo
pnext del nodo corrente*/
}

/*Dato in input il puntatore p_punt elimina il nodo successivo a


p_punt ovvero il nodo successivo a quello puntato da p_punt.
Per eliminare, basta scavalcare il nodo da eliminare aggangiando
il nodo puntato da p_punt al successore del nodo da eliminare*/
void elim_nodo(void *p_punt)
{
/* definisce una struttura generica che condivide con il main solo il tipo infofield e
dichiara un puntatore alla struttura */
struct lista
{
INFO_FIELD info;
struct lista *p_next;
}*ptr;

// prendi nodo successivo al nodo sentinella (o puntato da p_punt)


ptr=((struct lista*)p_punt)->p_next;

// Aggancia nodo p_punt al successivo di quello da eliminare


((struct lista*)p_punt)->p_next=ptr->p_next;

//libera ptr, ossia il successivo a sentinella eliminato


free(ptr);// elimina la testa della lista
}

Screenshot esercizio 44

Inserimento Pila Eliminazione Pila


- 58 -

/*In questo caso GESTIAMO LA CODA CON LA LISTA DINAMICA(CON NODO SENTINELLA)*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*stabiliamo la struttura che mi contiene le informazioni di ogni nodo: abbiamo l'unico campo nome*/
typedef struct{
char nome[20];
} INFO_FIELD;

void *crea_lista();//crea lista con il nodo sentinella


void visita(void *p_punt);
void ins_nodo(short len_info, INFO_FIELD *p_dato, void **p_punt);//inserimento
void elim_nodo(void *p_punt);//eliminazione
void main()
{
/*stabiliamo la struttura del singolo nodo*/
struct persona
{
INFO_FIELD info;/*un campo informazione che si chiama info ed e' di tipo
INFO_FIELD dichiarato sopra con una typedef*/
struct persona *p_next;/*p_next e' un puntatore autoriferente perche' all'interno della
struttura struct PERSONA c'e' un puntatore allo stesso tipo della struttura, quindi un
puntatore alla struct persona*/
};
typedef struct persona PERSONA;
int scelta;
PERSONA *head, *fondo;/*head e' il puntatore di testa e fondo e' il puntatore di fondo alla lista*/
short len_info;//lunghezza informazione

INFO_FIELD nuovodato;//campo informazioni che verrà inserito nel nuovo nodo da creare
len_info=sizeof(INFO_FIELD);

/*Il nodo sentinella viene inserito in testa come nodo vuoto.


Quando inserisco o elimino, lo farò da questo (funzionerà come ins_testa)
head, punta a questo nodo sentinella.*/
head = (PERSONA*)crea_lista();//verrà restituito un *void, quindi effettuiamo cast
fondo=head; //Inizialmente, fondo e head puntano al nodo sentinella

puts("\nCODA MEDIANTE LISTA DINAMICA \n");


do
{

printf("\n0 per l'uscita dal programma\n1 per la visualizzazione della coda \n2 per
l'inserimento nella coda enqueue()""\n3 per l'eliminazione dalla coda dequeue()");
printf("\nScelta: ");
scanf("%d",&scelta);
system("cls");
switch(scelta)
{
case 0:
printf("\nFINE PROGRAMMA!\n");
break;

case 1:
/*Stampa se dopo il nodo sentinella c'è qualcosa.
Inizia a stampare dal nodo successivo sentinella*/
if(head->p_next)
{
visita((void *)(head)->p_next); //stampa la lista dopo ogni operazione
break;
}
else
{
printf("\nLa coda e' vuota!\n");
break;

}
- 59 -

case 2:
//La CODA inserisce in fondo, ma estrae dalla testa
// Inserisce dopo nodo sentinella, inserendo il nodo dal fondo
printf("\nInserisci il nome ");
fflush(stdin);
gets(nuovodato.nome);
/* Fondo punta all'ultimo valore inserito.
Quando inserisco un elemento dopo fondo, fondo si aggiorna all'indirizzo
del nuovo nodo: quindi quando riinserirò a partire da quell'indirizzo
che indica il nodo alla fine, inserirà alla fine e fondo si riaggiornerà */
ins_nodo(len_info, &nuovodato, (void **)&fondo);
printf("Inserimento nella coda effettuato\n");
break;

case 3:
//Elimina il nodo dalla testa della coda
if (head->p_next!= NULL)// Se dopo il nodo sentinella c'è qualcosa
{
/* Head punta a sentinella, quindi elimina elemento dopo sentinella.
Head punterà sempre al nodo sentinella, quindi non necesito di modificarlo */
printf("Eliminazione in testa...\n");
elim_nodo((void *)head);
}
else
{
printf("\nPILA VUOTA - IMPOSSIBILE ELIMINARE ELEMENTI\n");
}
break;
}
}while(scelta!=0);
}

/*Crea la lista e restituisce un puntatore iniziale della testa


che e' vuoto. La testa punterà al nodo sentinella */
void *crea_lista()
{
struct lista
{ INFO_FIELD info;
struct lista *p_next;
};
struct lista *testa;// il tipo non importa, nel main ho il cast
testa=(struct lista*)calloc(1,sizeof(struct lista));
testa->p_next=NULL;//anche se con calloc posso evitare questa istruzione visto che calloc
inizializza a 0
return testa;
}

/*Date in input le informazioni da inserire, la loro lunghezza e il puntatore p_punt si inserisce in


fondo, dopo il nodo puntato da *punt, il nuovo nodo. Quindi il nuovo nodo punterà al nodo successivo di
p_punt ed il nodo p_punt punterà proprio al nuovo nodo. Ed aggiorniamo il fondo in modo che punta
all'ultimo valore inserito.*/
void ins_nodo(short len_info, INFO_FIELD *p_dato, void **p_punt)
{
/*si definisce una struttura lista generica che condivide con il main solo il tipo
INFO_FIELD(globale)*/
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr= calloc(1, sizeof(struct lista));//alloca 1 blocco di memoria della lunghezza della struct
inizializzando ptr a 0
memcpy(ptr, p_dato, len_info);/*copia len_info byte da *p_dato a *ptr senza interessarsi della
struttura delle informazioni*/
//aggancia il nuovo nodo al prossimo di p_punt
ptr->p_next = ((struct lista *)*p_punt)->p_next;/*colleghiamo il nuovo nodo con la vecchia testa:
il cast (struct lista *) e' obbligatorio prima di usare un puntatore generico, ma non ci vuole quando
lo si definisce*/
((struct lista*)*p_punt)->p_next= ptr; //aggiorna head al nuovo nodo
*p_punt=ptr;//aggiorniamo il fondo
}
- 60 -

/*questa funzione ci consente di visualizzare gli elementi contenuti sia nella lista che nella pila,
attraverso il puntatore punt*/
void visita(void *p_punt)
{
struct lista
{
INFO_FIELD info;
struct lista *p_next;
} *ptr;
ptr = ((struct lista*)p_punt);
while (ptr!= NULL)/*finche' non siamo arrivati alla fine della lista*/
{
printf("NOME = %s p_next= %d \n", (*ptr).info.nome,(*ptr).p_next);
ptr = ptr->p_next;/*assegna a punt il link (indice) del nodo successivo contenuto nel campo
pnext del nodo corrente*/
}
}

/*Dato in input il puntatore p_punt elimina il nodo successivo a


p_punt ovvero il nodo successivo a quello puntato da p_punt.
Per eliminare, basta scavalcare il nodo da eliminare aggangiando
il nodo puntato da p_punt al successore del nodo da eliminare*/
void elim_nodo(void *p_punt)
{
/* definisce una struttura generica che condivide con il main solo il tipo infofield e
dichiara un puntatore alla struttura */
struct lista
{
INFO_FIELD info;
struct lista *p_next;
}*ptr;

// prendi nodo successivo al nodo sentinella (o puntato da p_punt)


ptr=((struct lista*)p_punt)->p_next;

// Aggancia nodo p_punt al successivo di quello da eliminare


((struct lista*)p_punt)->p_next=ptr->p_next;

//libera ptr, ossia il successivo a sentinella eliminato


free(ptr);// elimina la testa della lista
}

Screenshot esercizio 44

Inserimento Coda Eliminazione Coda


- 61 -

/*ESERCIZIO 49
Scrivere le function C per la visita (preorder, inorder e postorder)
di un albero binario rappresentato mediante array*/
/*-VISITA PREORDER(ordine anticipato) che visita in ordine :
1)la radice
2)il sottoalbero sinistro
3)il sottoalbero destro
-VISITA INORDER(ordine simmetrico) che visita in ordine:
1)il sottoalbero sinistro
2)la radice
3)il sottoalbero destro
-VISITA POSTORDER(ordine differito) che visita in ordine:
1)il sottoalbero sinistro
2)il sottoalbero destro
3)la radice */

#include<stdio.h>
void visita_preorder(int array[],int i);
void visita_postorder(int array[],int i);
void visita_inorder(int array[],int i);

void main()
{
int i, j=0, num_nodi=7;
int array[7];
for(i=1;i<=7;i++)
{
array[i]=j;
j++;
}
printf(" ALBERO BINARIO\n\n");
printf(" (%d)\n",array[1]);
puts(" / \\");
puts(" / \\");
puts(" / \\");
printf(" (%d) (%d)\n",array[2],array[3]);
puts(" / \\ / \\ ");
printf(" (%d) (%d) (%d) (%d) \n",array[4],array[5],array[6],array[7]);
i=1;
j=0;
printf("\nVISITA PREORDER\n");
visita_preorder(array,i);
printf("\n\nVISITA POSTORDER\n");
visita_postorder(array,i);
printf("\n\nVISITA INORDER\n");
visita_inorder(array,i);
}
/*L'algoritmo di visita di un albero binario in generale deve prevedere una struttura dati di
appoggio, uno "STACK", il quale conserva l'informazione sull'ordine dei nodi di cui non e'
stata ancora completata la visita. Esso deve essere gestito direttamente dal programmatore
nell'algoritmo iterativo, mentre viene gestito dal linguaggio di programmazione quando
l'algoritmo e' descritto in maniera ricorsiva. Noi adottiamo la maniera ricorsiva*/

/*PREORDER (ordine anticipato)*/


void visita_preorder(int array[], int i)
{
if (i >7 ) /*soluzione del caso banale(quando ci troviamo che il nodo e' una foglia):
indice>dimensione albero*/
{
return;//l'algoritmo torna all'attivazione precedente lasciata in sospeso
}
printf("%d ",array[i]); /*stampa il nodo corrente dell'albero */

/*function ricorsiva relativa alla visita del sottoalbero sinistro finche' non e' stato visitato
fino alle foglie*/
visita_preorder(array, 2*i);

/*function ricorsiva relativa alla visita del sottoalbero destro finche' non e' stato visitato fino
alle foglie*/
visita_preorder(array, 2*i+1);
}
- 62 -

/*INORDER (ordine simmetrico)*/


void visita_inorder(int array[], int i)
{
if (i >7 ) /*soluzione banale: indice>dimensione albero*/
return; /*indica il ritorno al precedente processo lasciato in sospeso*/

/*function ricorsiva relativa al sottoalbero sinistro*/


visita_inorder(array, 2*i);

printf("%d ",array[i]); /*stampa il nodo corrente dell'albero*/

/*function ricorsiva relativa al sottoalbero destro*/


visita_inorder(array, 2*i+1);
}

/*POSTORDER (ordine posticipato 0) */


void visita_postorder(int array[], int i)
{
if (i >7 ) /*soluzione banale: indice>dimensione albero*/
return; /*indica il ritorno al precedente processo lasciato in sospeso*/

/*function ricorsiva relativa al sottoalbero sinistro*/


visita_postorder(array, 2*i);

/*function ricorsiva relativa al sottoalbero destro*/


visita_postorder(array, 2*i+1);

/*stampa nodo corrente*/


printf("%d ",array[i]);
}
Screenshot esercizio 49
- 63 -

/*ESERCIZIO 55
Scrivere function C per la costruzione di un grafo non orientato mediante matrice di adiacenze:
in input per ogni nodo sono specificati quelli adiacenti.
Scegliendo in input un nodo, scrivere una function C che restituisca il suo grado.*/
/*Ricordiamo alcune cose fondamentali sul grafo non orientato:
Il grafo non orientato e' un grafo in cui non e' stato assegnato un verso di percorrenza,
quindi la percorrenza da un nodo a un altro e' a doppio senso. La matrice adiacente,
che e' la rappresentazione matematica del grafo e' una matrice simmetrica: cio' significa che se
noi potessimo tagliarla sulla diagonale principale, il triangolo superiore è simmetrico rispetto al
triangolo inferiore */

#include<stdio.h>
/*dichiarazione della struttura globale del singolo nodo del grafo con il campo informazione che e' int
( ho preferito i numeri) e il campo grado di tipo intero*/
struct nodo
{
int info;
int grado;
};

typedef struct nodo NODO;/*definiamo il tipo NODO della struttura struct nodo*/
int num_nodi=0;/*variabile globale che mi indica il numero di nodi totali presenti nel grafo*/

void costruisci_grafo(int matrice_adiacenze[][],NODO nodi[],int num_nodi);


void grado_nodo(NODO nodi[]);
void main()
{
int i,j;/*i e j sono i contatori utilizzati per la matrice e per l'inizializzazione a 0 sia degli
elementi cotenuti del grafo sia del numero dei gradi*/
int matrice_adiacenze[num_nodi][num_nodi];/*questa e' la variabile relativa alla matrice di
adiacenze che ha due dimensioni pari al numero dei nodi contenuti nel grafo*/
puts("GRAFO NON ORIENTATO\n");

/*Finche' l'utente non inserisce almeno due nodi si continua all'infinito*/


do{
printf("Inserisci il numero di nodi (MIN 2): ");
fflush(stdin);
scanf("%d",&num_nodi); /*numero dei nodi scelto dall'utente*/
if(num_nodi<2)/*se non abbiamo inserito almeno 2 nodi*/
{
puts("ERRORE: Inserire almeno due nodi\n");
}

}while(num_nodi<2);
NODO nodi[num_nodi];/*variabile struttura di tipo array che ci serve per effettuare le varie
operazioni sulla matrice*/

/*inizializzo a 0 il valore del grado di ogni singolo nodo*/


for(i=0;i<num_nodi;i++)
{
nodi[i].grado=0;
}

/*chiamata che costruisce il grafo*/


costruisci_grafo(matrice_adiacenze,nodi,num_nodi);
}
- 64 -

void costruisci_grafo(int matrice_adiacenze[][],NODO nodi[],int num_nodi)


{

int i,j;
int adiacenza;/*questa variabile serve per definire le adiacenze scelte dall'utente*/
int k=1;/*k mostra all'utente le adiacenze(nodi rispettivi adiacenti)*/
char car=(char)65;/*questa variabile mi serve per capire meglio i passaggi definendo, per ogni
informazione di un singolo nodo, il relativo carattere. Inizializzo la variabile al primo
carattere dell'alfabeto la A maiuscola*/

/*inizializzo a 0 le componenti della matrice,


usando l'indice i per le righe e l'indice j per le colonne*/
for(i=0;i<num_nodi;i++)
{
for(j=0;j<num_nodi;j++)
{
matrice_adiacenze[i][j]=0;
}
}
/*Visualizzo il relativo carattere*/
for(i=0;i<num_nodi;i++)
{
printf(" %c ",car);
car++;/*incremento il valore di car cosi' da ottenere i successivi caratteri:
B,C,D,E,F,G...*/
}
printf("\n");
/*Visualizzo i nodi esistenti*/
for(i=0;i<num_nodi;i++)
{
nodi[i].info=i;
printf("[ %d ]",nodi[i].info);
}
printf("\n");
car=(char)64;/*inizializzo car al valore che precede la A maiuscola nel codice ASCII, cosi' che
al passaggio da un nodo a un altro, nell'inserimento delle adiacenze, possa relazionare il
valorenumerico al carattere relativo*/
/* Il for scala i nodi correnti al quale l'utente dovrà scegliere le adiacenze, partendo dal
primo nodo: il nodo 0.
/*Il do while itera mentre l'utente sceglie le adiacenze per ogni singolo nodo*/
for(i=0;i<num_nodi;i++)
{
car++;/*incrementiamo il valore di car a ogni fine del do while cosi' da visualizzare
il carattere relativo a ogni informazione del nodo*/
do{
printf("\nSpecificare %d-simo nodo adiacente a [%d](%c) (-1 per
terminare):",k,nodi[i].info,car);
fflush(stdin);
scanf("%d",&adiacenza);

/*Se il valore inserito è più alto del numero massimo o minimo dei nodi
oppure è uguale al nodo corrente, allora viene mostrato un ERRORE*/
if(adiacenza>=num_nodi || adiacenza<-1 || adiacenza==nodi[i].info)
{
printf("ERRORE: Valore non valido\n");
}
else if(adiacenza!=-1) /*se diverso dal valore di termine analisi del nodo*/
{
/*Controlla che la cella in cui stiamo inserendo 1, per indicare
l'adiacenza, sia vuota cioe' sia uguale a 0, altrimenti è già
esistente*/
if((matrice_adiacenze[i][adiacenza])==0)
{
(matrice_adiacenze[i][adiacenza])=1;/*inseriamo 1 nella cella che
ha per coordinate di righe il nodo corrente e per coordiante di
colonne il valore stesso dell'adiacenza*/
nodi[i].grado++;/*incrementiamo il valore del grado relativo al
nodo che stiamo considerando*/
k++;/*passiamo alla prossima adiacenza*/
}
- 65 -

else
{
puts("ATTENZIONE: Adiacenza gia' esistente. Reinserire!");
}
}
}while(adiacenza!=-1 && k<num_nodi);
k=1;/*inizializza il numero delle adiacenze nel momento in cui passiamo al prossimo
nodo*/
}

/*stampo in orizzontale i caratteri al fianco della matrice per dare chiarezza*/


printf(" ");
for(i=0;i<num_nodi;i++)
{
printf(" [%d]",i);
}

/*Stampa del grafo non orientato*/


for(i=0;i<num_nodi;i++)
{
printf("\n[%d] ",i);/*stampo in verticale i caratteri al fianco della matrice*/
for(j=0;j<num_nodi;j++)
{
printf("%d ",matrice_adiacenze[i][j]);
}
}
/*chiamo la function ver visualizzare il grado del nodo che voglio considerare*/
grado_nodo(nodi);
}

void grado_nodo(NODO nodi[])


{
int valore;
int scelta;
/*Ciclo che inserendo il numero del nodo di cui voglio conoscere i gradi, che si definiscono
come il numero di archi incidenti un nodo, e mi fa visualizzare il grado a esso relativo*/
do{
printf("\n\nDi quale nodo vuoi conoscere il grado? (-1 per terminare)\n-> ");
fflush(stdin); scanf("%d",&scelta);
valore=nodi[scelta].grado;
if(scelta!=-1)
{
printf("Il grado del nodo [%d] e' %d",scelta,valore);
}
}while(scelta!=-1);
}
Screenshot esercizio 55
- 66 -

/*ESERCIZIO 56
Scrivere function C per la costruzione di un grafo orientato mediante matrice di adiacenze:
in input per ogni nodo sono specificati quelli raggiungibili. Scegliendo in input un nodo,
scrivere una function C che restituisca il numero degli archi uscenti e quello degli archi entranti*/
/*Ricordiamo alcune cose fondamentali sul grafo orientato:
Il grafo orientato e' un grafo in cui e' stato assegnato un verso di percorrenza,
quindi la percorrenza da un nodo a un altro e' a senso unico. La matrice adiacente,
che e' la rappresentazione matematica del grafo e' una matrice non simmetrica: cio' significa che se
noi potessimo tagliarla sulla diagonale principale, il triangolo superiore non è simmetrico rispetto al
triangolo inferiore*/

#include<stdio.h>
/*dichiarazione della struttura globale del singolo nodo del grafo con il campo informazione che e' int
( ho preferito i numeri)
e il campo entrante e uscente di tipo intero relativi agli archi, che costituiscono le connessioni tra
due nodi adiacenti(dette anche lati)*/
struct nodo
{
int info;
int entrante;
int uscente;
};
typedef struct nodo NODO;/*definiamo il tipo NODO della struttura struct nodo*/
int num_nodi=0;/*variabile globale che mi indica il numero di nodi totali presenti nel grafo*/

void costruisci_grafo(int matrice_adiacenze[num_nodi][num_nodi],NODO nodi[num_nodi],int num_nodi);


void archi_nodo(NODO nodi[num_nodi]);
void main()
{
int i,j;/*i e j sono i contatori utilizzati per la matrice e per l'inizializzazione a 0 sia degli
elementi cotenuti del grafo sia del numero dei gradi*/

int matrice_adiacenze[num_nodi][num_nodi];/*questa e' la variabile relativa alla matrice di


adiacenze che ha due dimensioni pari al numero dei nodi contenuti nel grafo, ma non e' simmetrica*/

puts("GRAFO ORIENTATO\n");

/*Finche' l'utente non inserisce almeno due nodi si continua all'infinito*/


do{
printf("Inserisci il numero di nodi (MIN 2): ");
fflush(stdin);
scanf("%d",&num_nodi); /*numero dei nodi scelto dall'utente*/
if(num_nodi<2)/*se non abbiamo inserito almeno 2 nodi*/
{
puts("ERRORE: Inserire almeno due nodi\n");
}

}while(num_nodi<2);
NODO nodi[num_nodi];/*variabile struttura di tipo array che ci serve per effettuare le varie
operazioni sulla matrice*/

/*inizializzo a 0 il valore degli archi entranti e uscenti di ogni singolo nodo*/


for(i=0;i<num_nodi;i++)
{
nodi[i].entrante=0;
nodi[i].uscente=0;
}

/*chiamata che costruisce il grafo*/


costruisci_grafo(matrice_adiacenze,nodi,num_nodi);
}
- 67 -

void costruisci_grafo(int matrice_adiacenze[][],NODO nodi[],int num_nodi)


{

int i,j;
int raggiungibile;/*questa variabile serve per definire il nodo raggiungibile scelto dall'utente*/
int k=1;/*k mostra all'utente il numero in successione di nodi raggiungibili*/
char car=(char)65;/*questa variabile mi serve per capire meglio i passaggi definendo, per ogni
informazione di un singolo nodo, il relativo carattere. Inizializzo la variabile al primo carattere
dell'alfabeto la A maiuscola*/

/*inizializzo a 0 le componenti della matrice,


usando l'indice i per le righe e l'indice j per le colonne*/
for(i=0;i<num_nodi;i++)
{
for(j=0;j<num_nodi;j++)
{
matrice_adiacenze[i][j]=0;
}
}
/*Visualizzo il relativo carattere*/
for(i=0;i<num_nodi;i++)
{
printf(" %c ",car);
car++;/*incremento il valore di car cosi' da ottenere i successivi caratteri: B,C,D,E,F,G...*/
}
printf("\n");
/*Visualizzo i nodi esistenti*/
for(i=0;i<num_nodi;i++)
{
nodi[i].info=i;
printf("[ %d ]",nodi[i].info);
}
printf("\n");
car=(char)64;/*inizializzo car al valore che precede la A maiuscola nel codice ASCII, cosi' che
al passaggio da un nodo a un altro, nell'inserimento delle adiacenze, possa relazionare il
valore numerico al carattere relativo*/
/*Il for scala i nodi correnti al quale l'utente dovrà scegliere i nodi raggiungibili, partendo
dal primo nodo: il nodo 0.
Il do while itera mentre l'utente sceglie i nodi raggiungibili per ogni singolo nodo*/
for(i=0;i<num_nodi;i++)
{
car++;/*incrementiamo il valore di car a ogni fine del do while cosi' da visualizzare il
carattere relativo a ogni informazione del nodo*/
do{
printf("\nSpecificare %d-simo nodo raggiungibile da [%d](%c) (-1 per terminare):
",k,nodi[i].info,car);
fflush(stdin);
scanf("%d",&raggiungibile);

/*Se il valore inserito è più alto del numero massimo o minimo dei nodi
oppure è uguale al nodo corrente, allora viene mostrato un ERRORE*/
if(raggiungibile>=num_nodi || raggiungibile<-1 || raggiungibile==nodi[i].info)
{
printf("ERRORE: Valore non valido\n");
}
else if(raggiungibile!=-1) /*se invece è diverso dal valore di termine analisi del
nodo*/
{
/*Controlla che la cella in cui stiamo inserendo 1, per indicare il nodo
raggiungibile, sia vuota cioe' sia uguale a 0, altrimenti è già esistente*/
if((matrice_adiacenze[i][raggiungibile])==0)
{
(matrice_adiacenze[i][raggiungibile])=1;/*inseriamo 1 nella cella che ha
per coordinate di righe il nodo corrente e per coordiante di colonne il
numero del nodo raggiungibile*/
nodi[i].uscente++;/*incrementa il contatore dell'arco uscente del nodo
corrente*/
nodi[raggiungibile].entrante++;/*incrementa il contatore degli archi
entranti del nodo raggiunto*/
k++;/*passiamo al prossimo nodo raggiungibile*/
}
- 68 -

else
{
puts("ATTENZIONE: Nodo raggiungibile gia' esistente. Reinserire!");
}
}
}while(raggiungibile!=-1 && k<num_nodi);
k=1;/*inizializza il numero dei nodi raggiungibili nel momento in cui passiamo al prossimo
nodo*/
}

/*stampo in orizzontale i caratteri al fianco della matrice per dare chiarezza*/


printf(" ");
for(i=0;i<num_nodi;i++)
{
printf(" [%d]",i);
}

/*Stampa del grafo orientato*/


for(i=0;i<num_nodi;i++)
{
printf("\n[%d] ",i);/*stampo in verticale i caratteri al fianco della matrice per dare
chiarezza*/
for(j=0;j<num_nodi;j++)
{
printf("%d ",matrice_adiacenze[i][j]);
}
}
/*chiamo la function ver visualizzare il valore degli archi del nodo che voglio considerare*/
archi_nodo(nodi);
}

void archi_nodo(NODO nodi[])


{
int valore;
int scelta;
/*Ciclo che per input dell'utente mostra gli archi del nodo che si vuole conoscere*/
do{
printf("\n\nDi quale nodo vuoi conoscere gli archi? (-1 per terminare)\n-> ");
fflush(stdin);
scanf("%d",&scelta);
if(scelta!=-1)
{
printf("Gli archi del nodo [%d] sono:\nUscenti = %d\nEntranti =
%d",scelta,nodi[scelta].uscente,nodi[scelta].entrante);
}

}while(scelta!=-1);
}
Screenshot esercizio 56
- 69 -

/*ESERCIZIO 61
Scrivere delle function C (rispettivamente iterativa e ricorsiva) per calcolare
(con ricorsione sia lineare sia binaria) la somma delle componenti di un array.*/

#include<stdio.h>
int ricorsione_binaria(int array[],int primo, int ultimo);
int ricorsione_lineare(int array[],int n);
int somma_iterativa(int array[],int n);
void main()
{
int array[10]={0,1,2,3,4,5,6,7,8,9};
int n=10;/*n e' il size dell'array*/
int i;//contatore
int primo=0;//indice della prima componente del vettore
int ultimo=n-1;//indice della seconda componente del vettore
int somma_ite=0,somma_ric=0,somma_bin=0;/*variabili che,inizializzate tutte a 0, mi conterranno
il valore delle somme relative rispettivamente alla somma iterative, alla somma ricorsiva
lineare e alla somma ricorsiva binaria */
printf("Ecco gli elementi presenti nell'array\n");
for(i=0;i<10;i++)
{
printf("%d ",array[i],i);
}
somma_ite=somma_iterativa(array,n);
printf("\n\nSOMMA ITERATIVA: %d",somma_ite);
somma_ric=ricorsione_lineare(array,n);
printf("\n\nSOMMA RICORSIVA LINEARE: %d\n",somma_ric);
somma_bin=ricorsione_binaria(array,primo,ultimo);
printf("\nSOMMA RICORSIVA BINARIA: %d",somma_bin);
}

/*La ricorsione lineare prevede che nel corpo della funzione ci sia una sola chiamata ricorsiva*/
int ricorsione_lineare(int array[],int n)
{
if(n==1)/*caso banale*/
{
return array[0];/*il primo elemento dell'array e’ il risultato della somma*/
}
else
{
return array[n-1]+ricorsione_lineare(array,n-1);/*andiamo a sommare ricorsivamente
l'elemento contenuto nell'ultima componente dell'array a[9] con il penultimo e
con quelli precedenti, arriveremo alla prima componente a[0],dove risiede il risultato
della somma. Ad ogni chiamata ricorsiva si decrement il valore di n, il size dell'array
per passare agli elementi di indice minore*/
}
}

/*La function ricorsione binaria con l'approccio Divide et Impera che scinde l'istanza
principale in istanze piu' semplici per la risoluzione del problema.
Prevede 2 chiamate ricorsive all'interno del proprio corpo, il problema viene decomposto in
2 sottoproblemi, e poi la chiamata ricorsiva si applica ad entrambi i sottoproblemi le cui
soluzioni vanno sommate per ottenere il risultato finale.
La somma degli elementi di un vettore di n-elementi e' pari alla somma delle somme di due
vettori composti il primo dai primi n/2 elementi ed il secondo dagli altri n/2 elementi.*/
int ricorsione_binaria(int array[],int primo, int ultimo)
{
int mediano;//questo e' l'elemento di mezzo

if(primo==ultimo)//caso banale
{
return array[primo];
}
else
{
/* autoattivazioni */
mediano = (primo+ultimo)/2; /*calcola l'indice mediano per dividere l'array*/
/*sommiamo ricorsivamente le sottoporzioni venitesi a formare per ottenere la soluzione finale*/
return ricorsione_binaria(array,primo,mediano) + ricorsione_binaria(array,mediano+1, ultimo);
}
}
- 70 -

int somma_iterativa(int array[],int n)


{
int i,somma=0;//i e' un contatore e con una variabile di appoggio calcola la somma iterativa
for(i=0;i<10;i++)
{
somma=somma+array[i];
}
return somma;
}

Screenshot esercizio 61

/*ESERCIZIO 62
Scrivere delle function C (rispettivamente iterativa e ricorsiva) per calcolare
(con ricorsione sia lineare sia binaria) la potenza intera x^n di un numero reale.*/

#include<stdio.h>
#include<math.h>
int ricorsione_lineare(int x,int n);
int ricorsione_binaria(int x,int n);
int pot_iterativa(int x, int n);
void main()
{
int x=2;//questo e' il valore
int n=3;//questa e' la potenza alla quale viene elevato il valore
int i;//contatore
int numero=pow(2,3);

int pot_lin=0,pot_bin=0,pot_ite=0;/*queste variabili mi servono per visualizzare


rispettivamente:
-la potenza iterativa
-la potenza con la ricorsione lineare
-la potenza con la ricorsione binaria*/
printf("La potenza e' questa: (%d)^%d\n",x,n);
pot_ite=pot_iterativa(x,n);
printf("\nPOTENZA ITERATIVA: %d\n",pot_ite);
pot_lin=ricorsione_lineare(x,n);
printf("\nPOTENZA IN RICORSIONE LINEARE: %d\n",pot_lin);
pot_bin=ricorsione_binaria(x,n);
printf("\nPOTENZA IN RICORSIONE BINARIA: %d",pot_bin);
}

int ricorsione_lineare(int x,int n)


{
if(n==1)/*caso banale*/
{
return x;/*ritoniamo x quando siamo arrivati alla fine delle moltiplicazioni*/
}
else
{
return x*ricorsione_lineare(x,n-1);/*andiamo a moltiplicare x per se stesso per n volte
cioe' un numero di volte pari al valore dell'esponente, decrementando il valore di n
fino ad 1 ad ogni chiamata*/
}
}
- 71 -

int ricorsione_binaria(int x,int n)


{
if(n==1)//caso banale
{
return x;/*ritorniamo il valore di x*/
}
else
{
return ricorsione_lineare(x,n-1)*ricorsione_lineare(x,n-2);/*moltiplichiamo finche' non
abbiamo finito il numero di volte che dobbiamo moltiplicare*/
}
}

int pot_iterativa(int x, int n)


{
int i;
const int k=x;/*la variabile k e' una variabile di appoggio costante, inizializzata a x*/
/*iteriamo per il numero di volte che dobbiamo fare le moltipliazioni*/
for(i=1;i<n;i++)
{
x=x*k;/*ad ogni moltiplicazione x incrementa il suo valore nella moltiplicazione
mentre k che e' costante manterra' il valore iniziale di x in
modo da non compromettere il giusto andamento della potenza*/
}
return x;
}

Screenshot esercizio 62
- 72 -

/*ESERCIZIO 67
Scrivere due function C (rispettivamente iterativa e ricorsiva) per implementare l’algoritmo Selection
Sort
su un array di struttura, sia mediante scambi reali sia mediante scambi virtuali*/
/*Definiamo il concetto di...
-scambi reali: si hanno quando i dati sono fisicamente scambiati di posto. Per scambiare l'informazione
vera e propria si richiede di scambiare non solo la chiave ma tutte le informazioni collegate a quella
chiave.
-scambi virtuali: si hanno quando i dati non sono fisicamente scambiati perché si opera tramite
puntatori: questo si fa per evitare di perdere tempo se abbiamo dati cospicui. Noi abbiamo le
informazioni ma per accedere a queste informazioni c'è bisogno di un array ausiliario di puntatori e
quindi gli scambi avvengono su questo array di puntatori e non sull'array informazioni vero e proprio.
Accedendo all'array di puntatori è come se accedessimo all'array informazioni secondo il criterio
richiesto. */
/*IL SELECTION SORT EFFETTUA L'ORDINAMENTO DETERMINANDO A OGNI PASSO IL MINIMO O IL MASSIMO(DIPENDE
DALLA SCELTA DEL PROGRAMMATORE) DI UN SOTTOINSIEME DELL'ARRAY.
IL SELECTION SORT E':
* IN PLACE(senza l'uso di ulteriori aree di memoria)
* COMPLESSITA' DI SPAZIO = O(n) lineare
* COMPLESSITA' DI TEMPO = O((1/2)*n^2)-numero di confronti al piu' O(n)-numero di scambi */

#include<stdio.h>
#include<stdlib.h>
/*dichiarazione della struct struttura che contiene il campo info, relativo all'informazione contenuta
nell'array:
in questo caso l'informazione e' di tipo carattere riferendosi alle lettere maiuscole dell'alfabeto*/
struct struttura
{
char info;
}*pt[10];/* *pt[10] e' un array di puntatori alla struttura che e' essenziale per effettuare gli scambi
virtuali*/
typedef struct struttura STRUTTURA;/*definiamo il tipo STRUTTURA relativo alla struct struttura*/
void sel_it_vi(STRUTTURA *pt[],int n);
void sel_ri_vi(STRUTTURA *pt[],int start_i,int n);
void sel_ite_rea(STRUTTURA array[],int n);
void sel_ri_rea(STRUTTURA array[],int start_i,int n);
void main()
{
STRUTTURA array[10]={'C','E','F','G','A','B','H','I','O','Z'};/*questa e' la variabile
struttura di tipo array che contiene i 10 caratteri disordinati che vanno messi in ordine*/
pt[10]=malloc(sizeof(STRUTTURA));/*allochiamo dinamicamente la variabile che punta alla
struttura dandogli il size della struct struttura*/
int i,j,scelta;
int n=10;/*n e' il size del nostro array*/
int start_i=0;//e' una variabile che mi serve nell'implementazione dell'algoritmo nel caso
ricorsivo.start_i e' inizializzata a 0 perche' e' l'indice iniziale del nostro vettore
/*inseriamo gli elementi dell'array di struttura array nel'array di puntatori*/
for(i=0;i<10;i++)
{
pt[i]=&array[i];
}
printf("\n\nArray di partenza disordinato:\n");
//stampa dell'array disordinato
for(i=0;i<n;i++)
{
printf("%c ",array[i].info);
}
printf("\n\nMENU' SELECTION SORT");
printf("\n[1]Ordinamento con scambi reali");
printf("\n[2]Ordinamento con scambi virtuali");
printf("\n[3]Esci");
do
{

printf("\n\nScelta: ");
scanf("%d",&scelta);
- 73 -

switch(scelta)
{
case 1:
sel_ite_rea(array,n);
printf("\n\nFORMA ITERATIVA CON SCAMBI REALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",array[i].info);
}
sel_ri_rea(array,start_i,n);
printf("\n\nFORMA RICORSIVA CON SCAMBI REALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",array[i].info);
}
break;
case 2:
sel_it_vi(pt,n);
printf("\n\nFORMA ITERATIVA CON SCAMBI VIRTUALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",pt[i]->info);
}
sel_ri_vi(pt,start_i,n);
printf("\n\nFORMA RICORSIVA CON SCAMBI VIRTUALI:\n");
for(i=0;i<10;i++)
{
printf("%c ",pt[i]->info);
}
break;
case 3:printf("\nArrivederci!");
break;
}

}while(scelta!=3);
}

void sel_ite_rea(STRUTTURA array[],int n)


{
int min_array;//questo e' l'indice relativo alla casella in cui e' contenuto il minimo elemento
int i, j;
char temp;//variabile temporanea per scambiare gli elementi qualora questo servi a ordinare
l'array
/*questo for mi serve per analizzare tutte le porzioni dell'array in successione*/
for(i=0;i<n-1;i++)
{
min_array=i;/*l'indice del minimo elemento dell'array coincide inizialmente con
l'indice del primo elemento dell'array e poi con i suoi successivi
quando i passa all'elemento successivo*/
for(j=i+1;j<n;j++)/*j e' un altro contatore che mi serve a scorrere tutto l'array
partendo dall'indice successivo a quello relativo al valore minimo dell'array:
facendo questo mi è possibile confrontare tutti gli elementi dell'array
con il valore minimo in questione*/
{
if(array[j].info<array[min_array].info)/*se scorrendo l'array troviamo un
elemento minore dell'elemento che e' stato considerato il minimo attuale*/
{
min_array=j;/*uguagliando l'indice del valore minimo con l'indice
dell'elemento attuale l'elemento attuale diventa il nuovo minimo*/
}
}
/*se si e' trovato l'elemento minimo rispetto a tutti gli altri allora si scambia la
posizione del nuovo elemento minimo con quello che era precedentemente il minimo
considerato. Usiamo una variabile temporanea per evitare di perdere il valore contenuto
nelle celle dell'array*/
temp=array[i].info;
array[i].info=array[min_array].info;
array[min_array].info=temp;
}
}
- 74 -

void sel_ri_rea(STRUTTURA array[],int start_i,int n)


{
int j,temp;
int i_min;//la variabile relativa all'indice che contiene la variabile di valore minimo
if(start_i>n-1)/*se siamo arrivati alla fine del vettore, usciamo dalla funzione*/
{
return;
}
i_min=start_i;/*l'indice che contiene la variabile di valore minimo e' inizializzata
al valore della variabile start_i che ha valore inizialmente 0,
riferendosi al primo indice del vettore. Quando si effettua il primo
scambio, start_i avanza di 1 passando all'indice successivo,
che contiene un valore che viene considerato il nuovo minimo*/
for(j=start_i+1;j<n;j++)/*con questo for scorriamo volta per volta tutto il vettore finche' o
non si verifica uno scambio o, visto che non sono stati trovati elementi minori del minore
corrente*/
{
if(array[j].info<array[i_min].info)/*se l'elemento contenuto nell'indice corrente e'
minore dell'elemento considerato attualmente il minimo visto che ha l'indice i_min*/
{
i_min=j;/*l'indice che contiene il valore minimo e' uguagliato all'indice
dell'elemento corrente,facendolo diventare il nuovo minimo*/
}
}
/*effettuiamo lo scambio facendo uso di una variabile temporanea temp in modo da non perdere
alcun dato*/
temp=array[start_i].info;
array[start_i].info=array[i_min].info;
array[i_min].info=temp;
sel_ri_rea(array,start_i+1,n);/*la funzione si autorichiama ma adesso start_i viene
incrementato di 1 per passare all'elemento successivo dell'array: faremo questo
finche' il valore di start_i non sara' maggiore del size del vettore cioè finche' non abbiamo
preso in considerazione tutti gli elementi del vettore*/
}

void sel_it_vi(STRUTTURA *pt[],int n)


{
/*a differenza della funzione iterativa relativa agli scambi reali,
scambio gli indirizzi*/
int min_array;
int i, j;
STRUTTURA *temp;
for(i=0;i<n-1;i++)
{
min_array=i;
for(j=i+1;j<n;j++)
{
if(pt[j]->info<pt[min_array]->info)
{
min_array=j;
}
}
temp=pt[i];//memorizzo l'indirizzo di pt[start_i]
pt[i]=pt[min_array];//scambio l'indirizzo di pt[start_i] con l'indirizzo di pt[i_min]
pt[min_array]=temp;//adesso pt[i_min] ha l'indirizzo di pt[start_i]
}
}
- 75 -

void sel_ri_vi(STRUTTURA *pt[],int start_i,int n)


{
/*a differenza della funzione ricorsiva relativa agli scambi reali,
scambio gli indirizzi*/
int j;
int i_min;
STRUTTURA *temp;
if(start_i>n-1)
{
return;
}
i_min=start_i;
for(j=start_i+1;j<n;j++)
{
if(pt[j]->info<pt[i_min]->info)
{
i_min=j;
}
}
temp=pt[start_i];
pt[start_i]=pt[i_min];
pt[i_min]=temp;
sel_ri_vi(pt,start_i+1,n);
}
Screenshot esercizio 67

/*ESERCIZIO 69
Scrivere una function C per implementare l’algoritmo Bubble Sort su un array di struttura,
sia mediante scambi reali sia mediante scambi virtuali*/
/*Definiamo il concetto di...
-scambi reali: si hanno quando i dati sono fisicamente scambiati di posto. Per scambiare l'informazione
vera e propria si richiede di scambiare non solo la chiave ma tutte le informazioni collegate a quella
chiave.
-scambi virtuali: si hanno quando i dati non sono fisicamente scambiati perché si opera tramite
puntatori: questo si fa per evitare di perdere tempo se abbiamo dati cospicui. Noi abbiamo le
informazioni ma per accedere a queste informazioni c'è bisogno di un array ausiliario di puntatori e
quindi gli scambi avvengono su questo array di puntatori e non sull'array informazioni vero e proprio.
Accedendo all'array di puntatori è come se accedessimo all'array informazioni secondo il criterio
richiesto. */
/*IL BUBBLE SORT SCORRE IL VETTORE SEQUENZIALMENTE E SCAMBIA I DUE ELEMENTI VICINI SE NON SONO
NELL'ORDINE CORRETTO. QUESTA ITERAZIONE VIENE ESEGUITA PIU' VOLTE FINO A CHE CI SONO ELEMENTI IN ORDINE
ERRATO.
IL BUBBLE SORT E':
* IN PLACE
* COMPLESSITA' DI SPAZIO = O(n) lineare
* COMPLESSITA' DI TEMPO NEL CASO PEGGIORE = O((1/2)*n^2)-confronti e O((1/2)*n^2)-scambi
* COMPLESSITA' DI TEMPO NEL CASO MEDIO = O((1/2)*n^2)-confronti e O((1/2)*n^2)-scambi */

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

/*dichiarazione della struct struttura che contiene il campo info, relativo all'informazione contenuta
nell'array:in questo caso l'informazione e' di tipo intero riferendosi ai numeri*/
struct struttura
{
int info;
}*pt[8];/* *pt[8] e' un array di puntatori alla struttura che e' essenziale per effettuare gli scambi
virtuali*/
typedef struct struttura STRUTTURA;/*definiamo il tipo STRUTTURA relativo alla struct struttura*/
void scambi_reali(STRUTTURA array[], int n);
void scambi_virtuali(STRUTTURA *pt[], int n);
void main()
{
STRUTTURA array[8]={3,2,9,7,5,1,8,4};/*questa e' la variabile struttura di tipo array
che contiene gli 8 numeri disordinati che vanno messi in ordine*/
pt[8]=malloc(sizeof(STRUTTURA));/*allochiamo dinamicamente la variabile che punta alla
struttura dandogli il size della struct struttura*/
int i;
int n=8;//size del vettore
int scelta;
/*inseriamo gli elementi dell'array di struttura array nel'array di puntatori*/
for(i=0;i<8;i++)
{
pt[i]=&array[i];
}
//stampa dell'array disordinato
printf("\nArray di partenza disordinato:\n");
for(i=0;i<8;i++)
{
printf("%d ",array[i].info);
}
printf("\n\nMENU' BUBBLE SORT");
printf("\n[1]Ordinamento con scambi reali");
printf("\n[2]Ordinamento con scambi virtuali");
printf("\n[3]Esci dal programma");
do
{
printf("\n\nScelta: ");
scanf("%d",&scelta);
switch(scelta)
{
case 1:printf("\nORDINAMENTO CON SCAMBI REALI: \n");
scambi_reali(array,n);
for(i=0;i<8;i++)
{
printf("%d ",array[i].info);
}
break;
case 2:printf("\nORDINAMENTO CON SCAMBI VIRTUALI: \n");
scambi_virtuali(pt,n);
for(i=0;i<8;i++)
{
printf("%d ",pt[i]->info);
}

break;
case 3:printf("\nArrivederci!");
break;
}
}while(scelta!=3);
}
- 77 -

void scambi_reali(STRUTTURA array[], int n)


{
int i,j;
int temp;
/*questo ciclo piu' esterno viene ripetuto tante volte
quanti sono gli elementi del vettore meno 1 perche' quando
siamo arrivati all'ultimo, tutti gli altri elementi sono gia' stati
messi nella giusta posizione e di conseguenza l'elemento che
avanza e' gia' nella sua posizione*/
for(i=0;i<n-1;i++)
{
/*il ciclo for piu' interno e' quello che fa tutti i confronti
e quando trova con l'if due elementi che non sono nella giusta posizione
li scambia. L'indice j, tutte le volte ricomincia dall'inizio del
vettore e si ferma ogni volta 1 elemento prima della passata precedente.
Per effettuare gli scambi facciamo uso della tradizionale variabile
di scambio per non perdere degli elementi*/
for(j=0;j<n-i-1;j++)
{
if(array[j].info>array[j+1].info)
{
temp=array[j].info;
array[j].info=array[j+1].info;
array[j+1].info=temp;
}
}
}
}

void scambi_virtuali(STRUTTURA *pt[], int n)


{
/*a differenza della funzione relativa agli scambi reali,
qui scambio gli indirizzi*/
int i,j;
STRUTTURA *temp;
for(i=0;i<n-1;i++)
{
for(j=0;j<n-i-1;j++)
{
if(pt[j]->info>pt[j+1]->info)
{
temp=pt[j];
pt[j]=pt[j+1];
pt[j+1]=temp;
}
}
}
}
Screenshot esercizio 69
- 78 -

/*ESERCIZIO 70
Scrivere una function C per implementare l’algoritmo Insertion Sort su un array di struttura*/
/*L'INSERTION SORT, PER CAPIRLO, POSSIAMO IMMAGINARE DI ORDINARE IN MANIERA CRESCENTE LE CARTE DA
GIOCO. A OGNI PASSO, DOBBIAMO INSERIRE UNA NUOVA CARTA FACENDO EVENTUALMENTE SLITTARE LE CARTE FINO A
TROVARE IL POSTO GIUSTO PER LA NUOVA CARTA.
L’INSERTION SORT E’:
 IN PLACE
 COMPLESSITA’ DI SPAZIO = O(n) lineare
 COMPLESSITA’ DI TEMPO NEL CASO PEGGIORE = O((1/2)*n^2)-confronti e O((1/2)*n^2)-scambi
 COMPLESSITA’ DI TEMPO NEL CASO MIGLIORE = O(n)-confronti e O(n)-scambi*/

#include<stdio.h>
#include<stdlib.h>
struct struttura
{
int info;
};
typedef struct struttura STRUTTURA;
void main()
{
STRUTTURA array[8]={3,2,9,7,5,1,8,4};
int i,j;/*contatori di cui:
-l'indice i lo usiamo per scorrere da sinistra a destra il vettore
-l'indice j lo usiamo per confrontare a sinistra gli elementi del
vettore con la variabile el_da_ins per verificare se il valore
di quegli elementi e' maggiore*/
int el_da_ins;
int n=8;
printf("\nArray di partenza disordinato:\n");
for(i=0;i<8;i++)
{
printf("%d ",array[i].info);
}
/*il for esterno indica il numero di porzioni dell'array da ordinare in successione*/
for(i=1;i<n;i++)
{
el_da_ins=array[i].info;/*i e' l'indice dell'elemento da inserire al passo i-simo
nella corretta posizione*/
j=i-1;/*visto che devo confrontare con gli elementi di sinistra, j viene decrementato
di 1 rispetto a i*/
/*finche' non siamo arrivati all'indice iniziale dell'array e finche' l'elemento da
inserire e' minore dell'informazione corrente di indice j*/
while(j>=0&&el_da_ins<array[j].info)
{
array[j+1].info=array[j].info;/*immetti l'informazione dell'indice j-simo
successivo nell'indice precedente*/
j--;//decrementa l'indice j
}
array[j+1].info=el_da_ins;/*adesso passiamo ad inserire nella variabile temporanea
l'elemento successivo da confrontare*/
}

printf("\n\nIMPLEMENTAZIONE DELL'ALGORITMO INSERTION SORT\n");


printf("Array finale ordinato:\n");
for(i=0;i<8;i++)
{
printf("%d ",array[i].info);
}
}
Screenshot esercizio 70
- 79 -

/*ESERCIZIO 17
Scrivere una function C per valutare un polinomio mediante l’algoritmo di Horner. Applicare l’algoritmo
ai dati dell’esempio 3 nelle dispense calcolando l’errore relativo. Usare una versione dell’algoritmo a
precisione estesa per ottenere il valore “esatto” di riferimento.*/

/*L'algoritmo di HORNER è un algoritmo efficiente ed accurato:


la differenza più importante tra Horner e il metodo di valutazione tradizionale riguarda principalmente
la complessità computazionale, cioè il numero di operazioni elementari che si esegue in un algoritmo.
Principalmente si deve controllare il numero di moltiplicazioni e divisioni, perchè "pesano" di più
delle
addizioni e sottrazioni. Quindi meno moltiplicazioni fa un algoritmo meglio è!
Prendiamo un polinomio di grado n, l'algoritmo tradizionale del calcolo del polinomio impiega 2n
moltiplicazioni e
n addizioni. Invece l'algoritmo di Horner impiega n moltiplicazioni e n addizioni.*/

/*Il polinomio da analizzare e' il seguente:


P(x)=512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 -1.
Nella forma generale il polinomio con Horner si presenta cosi':
P(x)=a|n+x*(a|n-1+x*(a|n-2+x*(a|n-3+x*(...+x*(a|1+x*a|0))...)))*/

#include <stdio.h>
#include <math.h>
#define MAX 10
double ric_Horner(double coef[],double x, int grado,int index);
double ite_Horner(double coef[],double x, int grado);
double err_rel(double x, float flx);

void main()
{
int grado=MAX,i;//il grado massimo nel nostro caso e' 10
double coef[10];
double Err_relativo=0.0,x,risultato_Horner=0.0;
float float_risultato_Horner=0.0;
int index=0;//questa variabile e' fondamentale nel calcolo di Horner ricorsivo

printf("Il grado del polinomio e' %d\n",grado);


//permettiamo all'utente di inserire i coefficienti di ogni variabile
for(i=grado;i>=0;i--)
{
printf("\nInserisci il coefficiente di x^%d: ",i);
scanf("%lf",&coef[i]);
fflush(stdin);
}
//inseriamo il valore dell'incognita
printf("\nAdesso inserisci la x di P(x) per cui vuoi calcolare il polinomio: ");
scanf("%lf",&x);

risultato_Horner = ite_Horner(coef,x,grado);
printf("\nHORNER VERSIONE ITERATIVA P(%lf) = %lf",x,risultato_Horner);
risultato_Horner = ric_Horner(coef,x,grado,index);
printf("\nHORNER VERSIONE RICORSIVA P(%lf) = %lf",x,risultato_Horner);

float_risultato_Horner = (float)risultato_Horner;//calcoliamo l'approssimazione del risultato


Err_relativo = err_rel(risultato_Horner, float_risultato_Horner);

printf("\nDouble_Horner=%22.16e\nFloat_Horner=%22.16e\n",risultato_Horner,float_risultato_Horne
r);
printf("Errore Relativo = %22.16e\n",Err_relativo);

}
- 80 -

/*In versione iterativa la valutazione delle parentesi procede


da quella piu' interna a quella piu' esterna*/
double ite_Horner(double coef[],double x, int grado)
{
int i=grado;
long double ris=coef[i];/*il risultato ha il valore iniziale del
primo coefficiente, relativo al grado 10*/
/*scorriamo il polinomio dal primo coefficiente all'ultimo
e effettuiamo le dovute addizioni e moltiplicazioni*/
for(i=grado-1;i>=0;i--)
{
ris = x*ris+coef[i];
}
return ris;
}

/*Ricordiamo che l'errore relativo=|x-flx|/|x|


dove x e' il valore che vogliamo considerare e
flx una sua approssimazione*/
double err_rel(double x, float flx)
{
double err=0.0;
err=fabs((x-flx))/fabs(x);
return err;
}

/*In versione ricorsiva la valutazione delle parentesi procede


da quella piu' esterna a quella piu' interna*/
double ric_Horner(double coef[],double x, int grado,int index)
{
if(index==grado)
{
return coef[grado];
}
return coef[index]+x*ric_Horner(coef,x, grado,index+1);
}

Screenshot esercizio 17
- 81 -

/*ESERCIZIO 71
Scrivere function C per implementare l’algoritmo Merge Sort su un array in versione iterativa e
ricorsiva.*/
/*IL MERGE SORT CONFRONTA COPPIE DISGIUNTE DI ELEMENTI DEL VETTORE, CIOÈ È COME SE VEDESSE IL VETTORE
SUDDIVISO IN TANTE COPPIE: LA PRIMA-SECONDA, TERZA-QUARTA E COSÌ VIA, CHE VANNO CONFRONTATE ED
EVENTUALMENTE SCAMBIATE. LE PORZIONI ORDINATE CHE NE RISULTANO A DUE A DUE, SONO UNITE ATTRAVERSO MERGE
CON LE COPPIE ADIACENTI, FORMANDO SOTTOPORZIONI DI 4 ELEMENTI ORDINATE, E COSÌ VIA. QUESTO ALGORITMO
MERGE SORT RIAPPLICA L'ALGORITMO DI FUSIONE(MERGE) SU PORZIONI DELL'ARRAY E ALLA FINE CI TROVEREMO CON
L'INTERO VETTORE ORDINATO.
IL MERGE SORT E':
* NON IN PLACE
* COMPLESSITA' DI SPAZIO = O(n)+O(n) non lineare
* COMPLESSITA' DI TEMPO = O(n*log|2(n))-numero di confronti e O(n*log|2(n))-numero di scambi
ricordiamo che l'ordine O(n*log|2(n)) e' dato da questo prodotto:
[ Tfusione(n) x Num passi ]
O(n) x O(log|2(n)) */

#include<stdio.h>
#define N 8
void mergesort(int array[], int iniz, int fine);
void merge(int array[], int iniz, int mez, int fine);
void main()
{
int array[N]={3,2,9,7,4,1,8,5};
int n=N;//size dell'array
int iniz=0;//l'indice iniziale dell'array
int fine=N-1;//l'indice finale dell'array
int i;
printf("Array iniziale disordinato:\n");
for(i=0;i<N;i++)
{
printf("%d ",array[i]);
}
mergesort(array,0,N-1);//0 perche' e' l'inizio del vettore e N-1 la fine
printf("\n\nArray finale ordinato MERGE SORT :\n");
for(i=0;i<N;i++)
{
printf("%d ",array[i]);
}

/*MERGE SORT RICORSIVO: esso non e' in place perche' necessita di un'area di lavoro aggiuntiva detta
in questo caso la variabile b[N]*/
void mergesort(int array[], int iniz, int fine)
{
int i,j,k,m;
int mez;
if(iniz<fine)
{
mez=(iniz+fine)/2;//calcolo dell'indice elemento mediano
mergesort(array,iniz,mez);/*questa e' la chiamata relativa alla prima porzione del
vettore che va dall'inizio al mediano. Poi questa porzione viene divisa a sua volta in
due porzioni*/
mergesort(array,mez+1,fine);/*questa e' la chiamata relativa alla seconda porzione del
vettore che va dal mediano+1 alla fine. Poi questa porzione viene divisa a sua volta in
due porzioni*/
merge(array,iniz,mez,fine);/*adesso la funzione alla chiamata merge ci consente di
unire le due porzioni iniziali che hanno gli elementi ordinati tra loro ma ora gli
elementi di queste due porzioni vanno confrontati e se messi in disordine vanno prima
ordinati*/
}
else//caso banale
{
return;
}
}
- 82 -

void merge(int array[], int iniz, int mez, int fine)


{
int i, j, k;
int b[N];//vettore di appoggio
i = iniz;//i e' il contatore relativo all'inizio del vettore
j = mez+1;//j e' il primo indice contenuto nella seconda porzione del vettore
k = 0;
//fusione delle 2 meta'
while ((i<=mez) && (j<=fine)) /*finche' non abbiamo scorso la prima porzione e la seconda porzione
dall'inizio alla fine*/
{
if (array[i] <= array[j])/*se l'elemento di indice i contenuto nella prima porzione e'
minore/ugualedell'elemento di indice j contenuto nella seconda porzione*/
{
b[k] = array[i];/*metti l'elemento della prima porzione piu' piccolo nella variabile di
appoggio*/
i++;/*passiamo all'elemento successivo della prima porzione*/
}
else
{
b[k] = array[j];/*metti l'elemento della seconda porzione piu' piccolo nella variabile
di appoggio*/
j++;/*passiamo all'elemento successivo della seconda porzione*/
}
k++;/*incrementiamo l'indice della variabile d'appoggio per inserire i successivi elementi */
}

/*se i e' minore di mez significa che alcuni elementi


della prima meta' non sono stati inseriti nel vettore*/
while (i<=mez)
{
/*allora li aggiungo in coda al vettore*/
b[k] = array[i];
i++;
k++;
}

/*se j e' minore di fine significa che alcuni elementi


della seconda meta' non sono stati inseriti nel vettore*/
while (j<=fine)
{
/*allora li aggiungo in coda al vettore*/
b[k] = array[j];
j++;
k++;
}

/*alla fine copio il vettore di appoggio b nel vettore a*/


for (k=iniz; k<=fine; k++)
{
array[k] = b[k-iniz];
}
}
Screenshot esercizio 71
- 83 -

/*ESERCIZIO 73
Scrivere una function C per implementare l’algoritmo Quick Sort.*/
/*IL QUICK SORT È UN ALGORITMO DI ORDINAMENTO RICORSIVO IN PLACE CHE, COME IL MERGE SORT, SI BASA SUL
PARADIGMA
DIVIDE ET IMPERA. LA BASE DEL SUO FUNZIONAMENTO È L'UTILIZZO RICORSIVO DELLA PROCEDURA PARTITION: PRESO
UN ELEMENTO
DA UNA STRUTTURA DATI (ES. ARRAY) SI PONGONO GLI ELEMENTI MINORI A SINISTRA RISPETTO A QUESTO E GLI
ELEMENTI MAGGIORI
A DESTRA. IL QUICKSORT, TERMINE CHE TRADOTTO LETTERALMENTE IN ITALIANO INDICA ORDINAMENTO RAPIDO, È
L'ALGORITMO DI
ORDINAMENTO CHE HA, IN GENERALE, PRESTAZIONI MIGLIORI TRA QUELLI BASATI SU CONFRONTO.
IL QUICK SORT E':
* IN PLACE
* COMPLESSITA' DI SPAZIO = O(n) lineare
* COMPLESSITA' DI TEMPO NEL CASO PEGGIORE = o(n^2)-numero di confronti
* COMPLESSITA' DI TEMPO NEL CASO MIGLIORE = o(n*log|2(n))-numero di confronti*/

#include<stdio.h>
#define N 8
int partiziona(int a[], int inizio, int fine);
void quick_sort (int a[], int inizio, int fine);
void scambia(int a[], int x, int y);
void main()
{
int a[N]={3,2,9,7,4,1,8,5};
int inizio=0;//indice iniziale del vettore
int fine=N-1;//indice finale del vettore
int i;
printf("Array iniziale disordinato:\n");
for(i=0;i<N;i++)
{
printf("%d ",a[i]);
}
quick_sort(a,0,N-1);
printf("\n\nArray finale ordinato QUICK SORT:\n");
for(i=0;i<N;i++)
{
printf("%d ",a[i]);
}

/*Nella funzione ricorsiva quick_sort


abbiamo il vettore ed i 2 indici (Inizio e Fine).
Se ho almeno 2 elementi (Inizio<Fine) calcolo il pivot con
partiziona.
Il pivot, sarà il valore per cui i valori alla sua destra sono
maggiori e alla sua sinistra sono minori, quindi andiamo ad
ordinare da Inizio a i_Pivot-1 (PORZIONE DI SINISTRA) e da
i_Pivot+1 a Fine (PORZIONE DI DESTRA)*/
void quick_sort (int a[], int inizio, int fine)
{
int pivot;
if(inizio<fine)
{
pivot=partiziona(a,inizio,fine);//Trova il partizionatore
quick_sort(a,inizio,pivot-1);//Ordina la porzione a sinistra di partizionatore
quick_sort(a,pivot+1,fine);//Ordina la porzione a destra di partizionatore

}
else//caso banale
{
return;
}
}
- 84 -

int partiziona(int a[], int inizio, int fine)


{
int pivot=fine;//pivot sistematico: scegliamo come indice dell'elemento partizionatore quello
dell'indice finale del vettore per sistematicita’
int i=inizio;
int j=fine;

// Fin quando i e j non si incontrano


while(i<j)
{
/* Mentre a[i] <= al valore Pivot, da sinistra a destra, va bene e avanza i
Mentre a[j] >= al valore Pivot, da destra a sinistra, va bene e arretra j*/
while (a[i]<=a[pivot]&&i<j)
{
i++;//passiamo all'elemento successivo da sinistra
}

while (a[j]>=a[pivot]&&i<j)
{
j--;//passiamo all'elemento precedente da destra verso sinistra
}

if (i<j) /*se i e j, cioe' inf e sup non si sono scavalcati, cioe'


quando non ci sono piu' elementi da scambiare */
{
scambia(a,i,j); //se vengono trovati sia a[i] che a[j] allora si scambiano
}
}
scambia(a,i,pivot);//scambio il pivot nella sua posizione reale
//ritorna l'indice di dove si trova il partizionatore
return i;
}

void scambia (int a[], int x, int y)


{
int tmp=a[x]; //uso la variabile temporanea, essenziale per scambiare
a[x]=a[y];
a[y]=tmp;
}
Screenshot esercizio 73

Potrebbero piacerti anche