Recap
Recap
Output
L’output è il flusso di dati in uscita su schermo.
Ci sono varie funzioni che permettono di gestire l’output:
● Putchar(ch) :
o Questa funzione stampa a schermo il carattere contenuto nella variabile “ch”, se la variabile contiene un
intero verrà convertito secondo la tabella ascii.
o La funzione ritorna il carattere stampato, altrimenti ritorna EOF se c’è stato un errore.
● Printf(%format, arg1,arg2,…,argn) :
o Questa funzione stampa a schermo il contenuto dei vari argomenti ma in maniera più precisa e sicura
tramite le specifiche di conversione inserite in %format.
● Puts(str) :
oStampa la stringa “str” seguita dal carattere new line.
Direttive di formato
%d = intero %u = unsigned%s = stringa %c = char %x = esadecimale %o =
ottale %f = float %g = double %p = puntatore
Variabili
Una variabile è uno spazio di memoria modificabile.
Una variabile può essere:
● Locale quando può essere utilizzata solo nella zona del programma in cui è stata dichiarata.
● Globale quando può essere utilizzata in tutte le zone del programma.
Per poter utilizzare una variabile essa deve essere prima dichiarata: tipo_di_dato nome_varibile = valore.
Tipi di dato:
● Char = 1byte.
● Int = 4byte.
● Float = 4byte.
● Double = 8 byte.
● Variabili booleane: le variabili booleane possono essere usate nel programma richiamando la libreria
<stdbool.h> oppure senza includendo la libreria tramite la dichiarazione boole nome_varibile = true/false.
Costanti
Una costante è uno spazio della memoria RAM in cui viene memorizzato un determinato valore.
Input
L’input è il flusso di dati in entrata al computer.
Ci sono diverse funzioni che permettono di gestire l’input:
● Getchat(void):
o Legge un singolo carattere da tastiera.
o Restituisce il carattere letto.
● Scanf(format, &arg1,…,&argn) :
o Legge da tastiera i valori.
o I valori vengono formattati secondo le specifiche di conversione passate come primo parametro.
o Salva i valori formattati nelle variabili passate come secondo parametro.
o & è l’operatore unario di indirizzo, restituisce l’indirizzo di locazione in memoria dell’operando, in
questo caso permette alla funzione scanf di salvare i valori all’indirizzo di memoria della variabile.
o Tra il % ed il carattere di conversione può essere inserito un numero preceduto da un punto, il quale
indica la quantità massima di caratteri inseribili.
● Gets(str) :
o Legge una stringa da tastiera, compresi spazi e ritorno a capo che trasforma nel carattere terminatore.
o Str è la stringa in cui verrà salvato l’input.
● Fgets(str, n , stream) :
o Str è la stringa in cui verrà salvato quanto letto.
o N è il numero massimo di caratteri leggibili incluso il terminatore.
o Lo stream è un flusso di byte che permette al programma di comunicare con l’esterno.
▪ Stdin permette di inserire manualmente l’input tramite keyboard.
Operatori
Un operatore è un’istruzione che agisce sugli operandi.
Ci sono diversi tipi di operatori:
● Assegnazione ( = ) :
o permette di assegnare ad una variabile un’espressione (tutto ciò che restituisce un valore).
o Tipo_di_dato nome_variabile = espressione.
● Aritmetici :
o Permettono di fare calcoli tra operandi.
o + - * /
o % = operatore aritmetico di modulo : restituisce il resto della divisione tra 2 espressioni.
● Relazionali :
o Mettono in relazione due operandi.
o Restituiscono come valore 0 nel caso la relazione sia falsa.
o Restituiscono come valore 1 nel caso la relazione sia vera.
o > , < , >= , <= , == , !=.
● Logici :
o Permettono di collegare logicamente due o più relazioni.
o && = AND.
o || = OR.
o ! = NOT.
●Manipolatori di bit.
o Applicabili sono ad operandi di tipo intero, manipolano il dato a livello di singolo bit.
o L’operatore & esegue una AND bit per bit settando ad 1 in bit in una certa posizione quando entrambi i
bit in quella posizione nei due operandi valgono 1, altrimenti setta 0.
▪ Op1 = 63 = 00111111
▪ Da notare il fatto che l’operatore corrisponde ad una divisione intera per multipli di 2.
Op1 = 61 = 00111101
▪ Da notare che l’operazione di shift a sinistra si comporta come una moltiplicazione intera per
multipli di due.
Istruzioni
Le operazioni elementari sono dette istruzioni e si possono classificare in :
●Istruzioni non condizionate.
●Istruzioni condizionate : la loro esecuzione dipende da una condizione.
●Istruzioni di controllo : esprimo la condizione da cui dipende l’esecuzione delle istruzioni condizionate.
L’insieme delle istruzioni di controllo e controllate costituisce i costrutti:
● Cotrutto condizionale : insieme di condizioni e istruzioni condizionate
● Costrutto iterativo : insieme di istruzioni la cui esecuzione viene ripetuta sotto il controllo di opportune
istruzioni di controllo.
Istruzione di selezione singola If
L’istruzione if può essere usata singolarmente e qualora la condizione posta sia
vera le istruzioni al suo interno saranno eseguite.
È possibile gestire più condizioni mediante l’ausilio dell’else if posto tra l’if e
l’else.
Switch
L’istruzione di controllo switch è un costrutto a selezione multipla.
All’istruzione viene passata una variabile intera, la quale rappresenta
il numero del caso che verrà selezionato.
Alla fine di ogni caso non bisogna dimenticare l’istruzione di salto
“break”, quest’istruzione permette di uscire dallo switch senza che il
compilatore esegua e testi gli altri casi presenti. Ciò velocizza
l’esecuzione e risparmia risorse.
L’istruzione “default” è facoltativa ed ha il compito di specificare le
istruzioni da eseguire nel momento in cui non ci sia un caso che combaci con quanto richiesto dal codice.
While
Le istruzioni presenti nel corpo del ciclo verranno ripetute fin quando la condizione
resterà vera.
Bisogna fare in modo che ad un certo punto la condizione risulti falsa altrimenti ci si
ritrova in un ciclo infinito.
For
Il campo 1 è dedicato alla dichiarazione di variabili e/o inizializzazione
di variabili già create. Le variabili dichiarate in questo campo hanno
validità solo all’interno del for.
Do while
Il ciclo do while a differenza del while e del for è un ciclo post-condizionale, cioè,
valuta la condizione dopo le esecuzioni delle istruzioni presenti nel corpo.
Come col while “classico” il ciclo continuerà a ripersi fintanto che la condizione resta
vera.
Array
Una variabile di tipo array è un insieme omogeno di dati dello stesso tipo.
Dichiarazione = tipo nome [dimensione_max];
Un array può essere inizializzato in due modi:
●int array[10] = {elementi}; in questo modo la dimensione è specificata indipendentemente dagli elementi.
●Int array[] = {elementi}; in questo caso il compilatore deduce le dimensioni in base agli elementi.
Il riempimento di un array rappresenta lo spazio utilizzato e deve essere sempre <= della dimensione massima,
nel caso in cui si eccedesse si andrebbe incontro al segmentation fault.
Ogni elemento dell’array:
●Contiene un singolo dato
●È individuato da un numero progressivo detto indice che ne specifica la posizione all’interno dell’array.
o L’indice va da 0 a N-1 dove N è il numero degli elementi.
Architettura hardware
Quando si dichiara un array, il tipo di dato definisce quanti byte occorrono per
memorizzare ogni singolo elemento del vettore.
Spazio di memoria necessario per tutto l’array = sizeof(tipoarray) * dimensione_array.
Stringhe
Una stringa è un vettore di caratteri.
Le stringhe si stampano tramite il printf e la direttiva di formato %s
che stampa tutti i caratteri del vettore fino al carattere terminatore
“\0”.
Quando si legge una stringa si utilizza la funzione gets(), che a differenza di scanf, gets legge anche gli spazi.
Però gets ha “difetto” di non avere un limite di lettura, quindi, può essere inserita una quantità di caratteri che
va oltre la dimensione massima della stringa causando problemi di sicurezza e di affidabilità del programma.
Per una maggiore sicurezza e affidabilità del programma si utilizza fgets(str,n,stream).
Libreria string.h
Libreria contenente diverse funzioni utili per lavorare con le stringhe:
● Strcat(dest,sorg); concatena alla stringa “dest” la stringa “sorg”.
● Strncat(dest,sorg,n); concatena n caratteri della stringa sorg alla stringa dest.
● Strchr(str,c); restituisce il puntatore alla prima occorrenza del carattere c all’interno della stringa str.
● Strrchr(str,c); restituisce il puntatore all’ultima occorrenza del carattere c all’interno della stringa str.
● Strcmp(str1,str2); confronta le stringhe:
o Ritorna 0 se uguali.
o Ritorna >0 se str1<str2.
o Ritorna <0 se str1>str2.
● Strcpy(dest,sorg); copia la stringa sorg nella stringa dest.
● Strncpy(dest,sorg,n); copia n caratteri della stringa sorg nella stringa dest.
● Strlen(str); restituisce la lunghezza della stringa.
● Strspn(str1,str2); trova la prima occorrenza di un carattere in str1 che non è contenuto nei caratteri
specificati da str2, restituisce l’indice del primo carattere trovato.
● Strtok(str1,str2);
o suddivide la stringa in token separati dai delimitatori contenuti in str2.
o restituisce un puntatore al primo token trovato, se non rimangono più token restituisce NULL.
o Passando NULL come primo parametro si avanza nella suddivisione.
●Sscanf(str,”%”,&); legge l’input dalla stringa str.
●Strstr(str1,str2); restituisce il puntatore alla prima occorrenza della stringa str2 trovata in str1, oppure
NULL.
Matrici/array bidimensionali
Una matrice è un array bidimensionale composto da N righe e M colonne. Quando la
matrice ha lo stesso numero di righe e colonne si dice matrice quadrata.
La dichiarazione e l’inizializzazione seguono le stesse regole degli array
monodimensionali, con l’aggiunta logicamente della seconda dimensione. Bisogna
SEMPRE specificare le dimensioni, altrimenti il programma non verrà compilato.
In memoria la matrice viene memorizzata in maniera sequenziale.
Puntatori
Una variabile di tipo puntatore è designata a contenere l’indirizzo di memoria della variabile puntata, la quale
può essere di qualunque tipo.
Ad un puntatore può essere assegnato NULL quando non punta a nulla.
L’indirizzo di memoria è assegnato tramite utilizzo dell’operatore unario di indirizzo &.
Quando si dichiara un puntatore ad esso viene assegnato un tipo di dato, la variabile puntata dovrà essere dello
stesso tipo del puntatore.
I puntatori possono essre concatenati, si parla in questi casi di double o triple pointer : si parte da una variabile
che viene puntata da un primo puntatore, questo puntatore viene puntato a sua volta e così via, ogni volta che si
aumenta il livello di concatenazione va aggiunto un *.
Es: int x = 69; int *p1 = &x ; int **p2 =&p1 ; int ***p3 =&p2.
L’operatore unario di dereferenziazione * di un puntatore restituisce il valore della variabile puntata
dall’operando:
● Utilizzato a destra dell’assegnazione esegue un’operazione di estrazione : a = *p, assegna ad “a” il valore
della variabile puntata da “p”.
● Utilizzato a sinistra esegue un’operazione di inserimento : *p = a, assegna il valore di “a” alla variabile
puntata da “p”.
Operazioni su puntatori.
È possibile utilizzare la parola chiave const per impedire la modifica di un puntatore, nello specifico:
● Int * const p = &y; = blocco del valore di p.
● Const int * p = &y; = blocco del valore puntato da p.
Il valore assunto da un puntatore può essere visto come un numero intero che rappresenta un indirizzo di
memoria.
Le operazioni di somma tra un puntatore e un valore intero con risultato un puntatore e le operazioni di
sottrazione tra puntatori con risultato intero devono essere eseguite tenendo conto del tipo della variabile
puntata.
La dichiarazione di un puntatore comporta allocazione di memoria per la variabile puntatore ma non per la
variabile puntata.
Array e puntatori
Il nome di un array è in realtà un puntatore costante all’indirizzo di partenza dell’array stesso.
Dichiarando un array:
● Viene allocata memoria per la variabile puntatore.
● Viene allocata l’area puntata di lunghezza predefinita.
● Il puntatore è inizializzato con l’indirizzo del primo elemento dell’array.
Ogni elemento è accessibile:
● Tramite la dereferenziazione del puntatore-array incrementato di una quantità pari all’indice
dell’elemento. *(A+i).
● Tramite parentesi quadre. A[i].
Dato un array A[N] di indirizzo “ind”, A[i] il suo elemento i-esimo, l’indirizzo di A[i] sarà dato dall’espressione
*(ind+i).
Array di puntatori
I puntatori così come le altre variabili possono essere inseriti in un array: int * A[10]; = array di 10 puntatori a
int.
Così come il nome di un array è un puntatore, un array di puntatori è un puntatore a puntatore con in più
l’allocazione della memoria puntata.
Array di stringhe
Un caso di array di puntatori è quello dell’array di stringhe che consente anche l’inizializzazione delle stringhe
che costituiscono l’array.
Char * colori[3] = {“blu”, “rosso”, “bianco”};
● All’interno dell’array le stringhe possono avere lunghezza diversa.
● In memoria le stringhe sono allocate consecutivamente.
● Sono riservati tanti bytes quant’è la rispettiva lunghezza, terminatore compreso.
Ogni elemento dell’array è un puntatore ad un char, quindi in realtà l’array contiene soltanto i puntatori al
primo carattere di ogni stringa.
Sottoprogrammi / funzioni
Quasi si scrive un programma può esserci la necessità di ripetere numerose volte una stessa sequenza di
istruzioni, per facilitare la lettura e ridurre le dimensioni del programma si fa uso di una struttura chiamata
sottoprogramma.
Un sottoprogramma è un modulo di programma la cui esecuzione può essere richiamata più volte.
Un sottoprogramma può essere utilizzato per gestire diversi compiti, i risultati di tali operazioni saranno
comunicati al programma chiamante al termine dell’esecuzione.
I sottoprogrammi si dividono in due categorie in base al valore che ritornano:
● Si dicono procedure i sottoprogrammi che non ritornano un valore e che quindi hanno come tipo di dato il
void.
● Si dicono funzioni i sottoprogrammi che ritornano un valore al programma chiamante. A questo tipo di
sottoprogramma deve essere associato il tipo di dato che si vuole far ritornare al termine dell’esecuzione.
I sottoprogrammi vengono dichiarati nella 2° zona del programma (prima del main), la dichiarazione di una
funzione prende il nome di prototipo di funzione. Tipo_ritorno nome_funzione (tipo_par1, … , tipo_parN);
La funzione viene definita dopo il main; tipo_ritorno nome_funzione(tipo_par1 nome_par1, … , tipo_parN
nome_parN) { istruzioni }. È in questa parte del programma che viene scritto il corpo della funzione. Il nome dei
parametri può anche essere diverso rispetto a come sono stati passati. Per far tornare i dati al chiamante si
utilizza return.
La chiamata di funzione è quando nel main viene invocata la funzione, prima di chiamare la funzione essa deve
essere almeno stata dichiarata altrimenti il programma darà errore. Quando si chiama una funzione i parametri
devono essere passati secondo un preciso ordine rispettandone il tipo. La chiamata può avvenire anche in un
printf senza l’utilizzo di una variabile a cui assegnare il valore ritornato.
Quando una funzione viene chiamata:
1. Viene allocato spazio per i parametri formali (quelli usati dalla funzione).
2. Viene copiato il valore dei parametri effettivi (quelli del main) nei parametri formali.
3. Viene allocato spazio per le variabili locali.
4. Viene eseguita la funzione.
5. Viene dealocato lo spazio delle variabili locali.
6. Viene dealocato lo spazio dei parametri formali.
7. Si procede con l’esecuzione del programma chiamante.
Includendo il file header stdlib.h si ha accesso a diverse funzioni che permettono di gestire dinamicamente la
memoria.
File
Un file è un’unità di memorizzazione dati.
I dati vengono memorizzati come una serie di bit, però a seconda del tipo di codifica si hanno:
● File di testo i quali contengono solo dati testuali, rappresentati da dei caratteri ASCII.
● File binari possono contenere qualsiasi tipo di dati, codificato in codice binario, a scopo di archiviazione o
di utilizzo.
Salvare i file in binario ha diversi vantaggi tra cui; le minori dimensioni, facilità di modificare il file e la facilita di
riposizionarsi nel file, però hanno anche degli svantaggi, ad esempio, la non portabilità da un tipo di calcolatore
ad un altro e il fatto che non si può creare o modifica un file binario usando un editor di testo.
I file all’interno del programma vengono gestiti tramite un puntatore a file. FILE *fp; (file pointer).
Apertura di un file
Tramite l’operazione di apertura viene creato un canale tra il programma ed il file nel filesystem.
La funzione adibita all’apertura del file è fp = fopen(“input.txt”,”modalità di lettura”);
● La funzione apre il file di nome input.txt
● L’operazione restituisce la variabile puntatore a file posizionata all’inizio del file input.txt.
● In caso di errore la funzione ritorna NULL.
Ci sono diverse modalità di apertura in base a cosa si deve fare:
Chiusura del file
L’operazione di chiusura svuota il canale e rilascia le risorse usate per la gestione del file.
È importate chiudere il file per non avere risorse occupate inutilmente, inoltre la chiusura garantisce la scrittura
del file nel disco.
La funzione che si occupa di questa operazione è fclose(fp);
Scrittura
Tramite l’operazione di scrittura vengono inviati bytes dal programma al filesystem.
La scrittura si effettua tramite la funzione fwrite, n = fwrite(buf, dimensione,elementi,fp);
● N è il numero di elementi effettivamente scritti, nel caso in cui n=-1 si è verificato un errore.
● Buf è il vettore che contiene i dati da memorizzare sul file.
● Dimensione è la grandezza in bytes di un elemento del vettore.
● Elementi indica il numero di elementi del vettore.
● Fp è il puntatore a file su cui si scrive.
Lettura
Tramite l’operazione di lettura vengono inviati bytes dal filesystem al programma.
La funzione che si occupa della lettura è fread, n = fread(buf,dimensione,elementi,fp);
● N è il numero di elementi letti. Se ha valore negativo significa che c’è stato un errore.
● Buf è il vettore dove vengono trasferite le informazioni lette dal file.
● Dimensione è la grandezza in bytes di un elemento da leggere.
● Elementi = numero di elementi da leggere.
● Fp è il puntatore al file da leggere.
Per i file di grandi dimensioni bisogna usare questa funzione più volte, non c’è rischio di perdere dati poiché le
operazioni di lettura accedono al file in maniera sequenziale; quindi, il puntatore si sposterà all’interno del file
di tanti bytes quanti sono quelli letti.
Quando tutto il contenuto è stato letto la funzione ritorna 0.
Per sapere la posizione del puntatore all’interno del file si utilizza la funzione fteel, n = fteel(fp); n sarà uguale
alla posizione del puntatore all’interno del file, se n<0 c’è stato un errore.
Altre funzioni
Fprintf(fp,format); funzione analoga al printf ma stampa sul file.
Fscanf(fp,format); funzione analoga allo scanf ma legge dal file.
Fputs(“stringa”,fp); stampa la stringa sul file.
Fgets(vet,DIM,fp); dim è il numero massimo di caratteri da leggere, vet è il vettore in cui salvare.
Fputc(c,fp); scrive il carattre c nel file.
Fgetc(fp); legge un carattere dal file.
Rewind(fp); permette di tornare all’inizio del file.
Typedef
L’istruzione typedef tipo nome_tipo; può essere usata per cambiare il nome ad un tipo di dato, questa modifica
funziona solo dove viene dichiarato.
Typedef int intero;
Enum
Il tipo enumerativo è un particolare tipo di dato che contiene un elenco di costanti, ciascuna associata ad un
valore intero.
Enum nome_enum{costante1,…,costanteN};
Struct
La stuct è un costrutto che consente la definizione di una struttura dati, costituita da campi indentificati da un
nome che possono essere di tipo diverso.
La struttura viene definita nel momento in cui il programma viene compilato però è vuota, le informazioni
verranno aggiunte al suo interno durante l’esecuzione del programma.
La struct si definisce prima del main nella stessa zona dei prototipi di funzione:
L’operazione di dichiarazione della variabile di tipo struttura può essere semplificata scrivendo solo il nome
della variabile tra la } e il ;
È possibile annidare le struct inserendo come membro una variabile di tipo struct appartenente ad un'altra
struttura.
Per accedere ai campi della struttura si utilizza l’operatore punto “.”
Struct e puntatori
I puntatori possono essere anche di tipo struttura ed essere usati per puntare ai membri della struct.
Dichiarazione = struct car *puntatore.
Iniziallizzazione = puntatore = &macchine.
Per accedere ai membri della stuct tramite il puntatore si possono utilizzare due notazioni equivalenti;
1. (*puntatore).marca
2. Puntatore->marca
In entrambe le notazioni stiamo puntado il valore del campo marca.
Salvataggio della struct su un file
Il salvataggio su un file può avvenire in due modi:
1. Tramite un vettore di tipo struttura. Es struct car vet[4];
2. Tramite un puntatore. Es struct car *macchine = malloc(sizeof(struct car));
Ricorsione
Una funzione viene detta ricorsiva quando nel suo corpo richiama se stessa.
● Ricorsione diretta: la procedura invoca direttamente nel suo corpo se stessa
● Ricorsione indiretta : la procedura invoca un’altra procedura che a sua volta chiama la procedura originale.
Le funzioni ricorsive si basano sul principio di induzione che dice : consideriamo una proprietà P(x) che si basa
sui numeri naturali (x) e ipotizziamo che p(x) sia vera per x = 1 e per un valore generico n. se a partire dalla
verità di P(n) riusciamo a dimostrare la verita di P(n+1) allora P(k) è vera per qualsiasi valore di k.
Basandoci su questo ci si approccia ad una funzione ricorsiva:
● Divide: a partire da un problema da risolvere, si individuano problemi del medesimo tipo di quello
originale ma aventi dimensioni ridotte. La suddivisione va ripetuta fino ad arrivare ai casi base, cioè
problemi di qui si conosce la soluzione.
● Impera : si risolvono i sotto-problemi singolarmente.
● Combina : si combinano le soluzioni dei sotto-problemi per arrivare alla soluzione del problema iniziale.
Esistono vari tipi di ricorsione, oltre a quella diretta ed indiretta:
● Ricorsione lineare : nel corpo dell’algoritmo è presente una sola chiamata a sé stesso.
● Ricorsione in coda : la chiamata ricorsiva è l’ultima istruzione dell’algoritmo stesso.
● Ricorsione multipla : se nel suo corpo sono presenti più chiamate a se stesso.
● Ricorsione muta : se è composto da una prima funzione che al suo interno ne chiama una seconda che a sua
volta richiama la prima.
● Ricorsione annidata: è un algoritmo composto da una funzione che ha come argomento una chiamata alla
funzione stessa.
Rand
La funzione rand contenuta nella libreria stdlib.h restituisce un numero casuale compreso nel range che va da 0
a RAND_MAX, quest’ultima è una costante di valore pari al numero 32767.
Int r = rand();
Utilizzando l’operatore % è possibile indicare un nuovo range da cui generare un numero casuale.
Int r = rand() % 101;
Se la funzione viene utilizzata da sola, ad ogni esecuzione del programma verrà generato lo stesso valore.
Questo è dovuto dal fatto che viene utilizzato lo stesso seed per la generazione del numero. Per ovviare a questa
cosa si utilizza la funzione srand(); tramite la quale è possibile specificare il seed.
Per utilizzare un seed diverso ad ogni esecuzione del programma si fa uso della funzione time(NULL), contenuta
nella libreria time.h, che restituisce l’ora corrente.
Diagramma di flusso
Il diagramma di flusso è un diagramma che descrive una sequenza mediante diverse forme geometriche, le quali
definiscono il tipo di passaggio, e tramite delle frecce che definiscono il flusso e la sequenza.
Simbolo del processo, rappresenta un’azione o un’istruzione.
Simbolo di inizio/fine.
Simbolo di decisione, indica una domanda a cui di solito si può rispondere con sì o no.
Simbolo di ingresso/uscita.
Complessità computazionale
Dato un problema è possibile determinare un insieme finito di algoritmi in grado di risolverlo.
La complessità computazionale permette di calcolare l’algoritmo più efficiente in baso alla relazione tra il
tempo di esecuzione e le dimensioni dei dati in ingresso.
Essendo il tempo effettivi di esecuzione soggetto a molti fattori, per calcolare l’efficienza dell’algoritmo viene
dato un costo ad ogni istruzione e sommando il costo di tutte le istruzioni otterremo il costo totale che però non
tiene conto del numero di variabili in ingresso.
Bisogna quindi usare una funzione che tenga conto dei dati in ingresso. Un metodo per calcolare la complessità
computazionale è l’analisi asintotica, cioè una funzione che tende all’infinito.
Gradi di complessità
Costante Logaritmica Lineare Quadratica Esponenziale
Fattoriale