4 - PUNTATORI
versione 13/01/2013
|
Costruire un programma che richiede un numero N e genera N valori interi casuali compresi tra 1 e 100. Al termina stampa l'elenco di quelli
pari. L'area di memoria deve essere gestita dinamicamente mediante l'uso di puntatori a integer.
|
|
SOLUZIONE:
OSSERVAZIONI:
void *malloc(size_t size); => Alloca un blocco di size bytes di memoria e ne restituisce l'indirizzo di inizio di tale area.
In caso di errore restituisce NULL. size_t corrisponde ad un unsigned int.
malloc (deriva da memory allocation) è una funzione delle librerie standard nei linguaggi di programmazione C e C++ per
l'allocazione dinamica della memoria. In caso di successo, malloc restituisce un puntatore void (void *) che indica che si tratta
di un puntatore ad una regione di dati di tipo sconosciuto. Un cast esplicito è a volte presente ma non è necessario nello standard C.
La sua omissione causa tuttavia una incompatibilità con il C++, che invece lo richiede. Richiede <stdlib.h>.
void free ( void * ptr ); => Dealloca il blocco di memoria, puntato dal parametro ptr, precedentemente allocato con i comandi
malloc, calloc o realloc. Se ptr non punta ad una zona di memoria allocata dinamicamente il suo comportamento è indefinito.
Se ptr è NULL allora free non fa nulla. Si ricordi che free non cambia il valore di ptr che quindi continua a puntare alla
stessa zona che però non è più valida. Richiede <stdlib.h>.
Un errore abbastanza frequente (specie se si ha a che fare con vettori di puntatori) è quello di chiamare free più di una volta sullo stesso
puntatore; per evitare questo problema una soluzione di ripiego è quella di assegnare sempre a null ogni puntatore liberato con free,
dato che, quando il parametro è un puntatore nullo, free non esegue nessuna operazione. Richiede <stdlib.h>.
void exit ( int status ); => Termina il processo correttamente ed esegue le regolari operazioni di chiusura per terminare il programma.
Lo stato zero o EXIT_SUCCESS, indica successo mentre lo stato EXIT_FAILURE indica una terminazione non corretta.
/**************************************************************************
* Nome: lab1-pari.c *
* Autore: Alessandro Saetti *
* Data: 6/4/10 *
**************************************************************************/
#include <stdio.h>
#include <stdlib.h> // richiesta da malloc() e system()
/*
* Nome: main
* Scopo: Visualizza i numeri pari contenuti in un vettore allocato dinamicamente
* Input: --
* Output: 0 se il programma termina correttamente
*/
int main()
{
int n, i, *v;
// INPUT - INIZIALIZZAZIONE
printf("Digita quanti valori, da 1 a 100, vuoi generare: ");
scanf("%d", &n);
// Allocazione di memoria dinamica con Casting
v = (int *)malloc(n * sizeof(int));
if (v == NULL) exit(0);
for (i = 0 ; i < n ; i++)
v[i] = rand() % 100 + 1;
// ALGORITMO + OUTPUT
printf("\nNumeri generati:\n");
for (i = 0 ; i < n ; i++)
printf("%4d", v[i]);
printf("\nNumeri pari:\n");
for (i = 0; i < n; i++)
if (v[i] % 2 == 0)
printf("%4d", v[i]);
printf("\n");
free(v); /* Liberiamo lo spazio di memoria allocato. */
v = NULL; /* Il puntatore non può essere più usato fino
ad un riassegnamento effettuato con malloc. */
system("pause");
return 0;
}
|
Costruire un programma che richiede una sequenza di punti (conclusa con la coppia 0 0) e mostra quelli che appartengono
al primo quadrante.
|
|
SOLUZIONE:
OSSERVAZIONI:
void * realloc ( void * ptr, size_t size ); => La funzione realloc() prevede due argomenti:
il primo ptr è un indirizzo di memoria, il secondo size specifica la nuova dimensione del blocco.
Il tipo restituito è un tipo puntatore a void al quale viene generalmente applicata un'operazione di casting. Richiede <stdlib.h>.
Viene utilizzata quando si vuol far crescere o ridurre un'area di memoria senza perdere i valori ivi registrati.
Realloc() restituisce un puntatore ad una zona di memoria con la dimensione specificata. Tale zona contiene gli stessi dati
della vecchia regione indirizzata da ptr (troncata alla fine nel caso la nuova dimensione sia minore di quella precedente).
Se realloc non è in grado di ridimensionare utilizzando ancora lo spazio di memoria originale, allocherà una nuova area
con le nuove dimensioni, copierà i dati richiesti, e libererà il vecchio puntatore.
La funzione realloc() è equivalente alla sequente sequenza:
/**************************************************************************
* Nome: lab2-punti.c *
* Autore: Alessandro Saetti *
* Data: 6/4/10 *
**************************************************************************/
#include <stdio.h>
#include <stdlib.h> // richiesta da realloc() e system()
/*
* Nome: main
* Scopo: Visualizza i punti del piano cartesiano contenuti in un vettore
* allocato dinamicamente e nel primo quadrante del piano cartesiano
* Input: --
* Output: 0 se il programma termina correttamente
*/
// Dichiaro il tipo predefinito x la struttura e per il suo puntatore.
// Per il puntatore potevo scrivere anche:
// typedef tPunto *tPuntoPtr;
typedef struct
{
int x, y;
} tPunto, *tPuntoPtr;
int main()
{
int n = 0, i;
//tPuntoPtr p = NULL;
// Oppure
tPunto *p = NULL;
// INPUT
printf("Digita le coordinate separandole con lo spazio (0 0 per finire)\n");
do
{
// Man mano leggo aggiungo dinamicamente la memoria necessaria
p = (tPunto *)realloc(p, (n + 1) * sizeof(tPunto));
// potevo scrivere anche:
// p = (tPuntoPtr)realloc(p, (n + 1) * sizeof(tPunto));
if (p == NULL)
exit(0);
printf("%d punto: ",n+1);
// potevo scrivere anche:
// scanf("%d %d", &(p+n)->x, &(p+n)->y);
scanf("%d %d", &p[n].x, &p[n].y);
n++;
}
while(p[n-1].x != 0 || p[n-1].y != 0);
// ALGORITMO - OUTPUT
printf("I punti nel 1^ quadrante sono:\n");
for (i = 0; i < n; i++)
if (p[i].x > 0 && p[i].y > 0)
printf("(%d,%d)\n", p[i].x, p[i].y);
system("pause");
return 0;
}
|
Costruire un programma che legge il nome e tempo di una sequenza di maratoneti e al termine li stampa a video.
La sequenza termina quando si digita 0:0:0
|
|
SOLUZIONE:
OSSERVAZIONI:
1) Per le prove usa il seguente elenco di parole (cut & paste).
1:10:23
Verdi
0:59:2
Gialli
0:59:2
Bianchi
1:1:21
Barbagrossa
1:10:2
Rossi
0:0:0
/**************************************************************************
* Nome: lab3-maratoneta.c *
* Autore: Alessandro Saetti *
* Data: 6/4/10 *
**************************************************************************/
#include <stdio.h>
#include <stdlib.h> // richiesta da realloc() e system()
typedef struct
{
int h, m, s;
} tTempo;
typedef struct
{
char nome[20];
tTempo t;
} tMaratoneta, *tMaratonetaPtr;
/*
* Nome: inizializzaMaratoneti
* Scopo: Inizializza un vettore di maratoneti con dati acquisiti da tastiera
* Non ho limite relativamente al numero di atleti da registrare
* Input: int *n: contiene il nr di atleti letti
* Output: restituisce il puntatore al vettore di maratoneti
* allocato dinamicamente
*/
tMaratonetaPtr inizializzaMaratoneti(int *n)
{
tMaratonetaPtr p = NULL;
bool bastaAtleti;
printf("Digita i dati (0:0:0 per terminare):\n");
printf("--------------------------------------------------\n");
do
{
p = (tMaratonetaPtr)realloc(p, (*n + 1) * sizeof(tMaratoneta));
if (p == NULL) exit(0); // L'allocazione è fallita
printf("Tempo (hh:mm:ss): ");
scanf("%d:%d:%d", &p[*n].t.h, &p[*n].t.m, &p[*n].t.s);
bastaAtleti=( (p[*n].t.h==0) && (p[*n].t.m==0) && (p[*n].t.s==0) );
if (!bastaAtleti)
{
printf("Nome: ");
scanf("%s", p[*n].nome);
*n = *n + 1;
}
printf("\n");
}
while (!bastaAtleti);
// while (p[*n-1].t.h != 0 || p[*n-1].t.m != 0 || p[*n-1].t.s != 0);
printf("--------------------------------------------------\n");
return p;
}
/*
* Nome: visualizzaMaratoneti
* Scopo: Visualizza un vettore di n maratoneti
* Input: Maratoneta v[]: un riferimento al vettore da visualizzare
* int n: la lunghezza del vettore
* Output: -
*/
void visualizzaMaratoneti(tMaratoneta v[], int n)
{
int i;
printf("Elenco atleti:\n");
for (i = 0; i < n; i++)
printf("%-20s %02d:%02d:%02d\n", v[i].nome, v[i].t.h, v[i].t.m, v[i].t.s);
printf("\n");
}
/*
* Nome: main
* Scopo: Inizializza e visualizza i dati contenuti in un vettore di maratoneti
* allocato dinamicamnte
* Input: --
* Output: 0 se il programma termina correttamente
*/
int main()
{
tMaratoneta *v = NULL;
int n = 0;
//INPUT
v = inizializzaMaratoneti(&n);
//OUTPUT
visualizzaMaratoneti(v, n);
system("pause");
return 0;
}
|
Costruire un programma che legge il nome e tempo di una sequenza di maratoneti e al termine mostra quello che ha il tempo migliore.
La sequenza termina quando si digita 0:0:0
|
|
SOLUZIONE:
OSSERVAZIONI:
1) Per le prove usa il seguente elenco di parole (cut & paste).
1:10:23
Verdi
0:59:1
Gialli
0:59:2
Bianchi
1:1:21
Barbagrossa
1:10:2
Rossi
0:0:0
2) Le strutture possono essere inizializzate in questo modo
tMaratoneta best = {"", {100, 60, 60}}; // singolo elemento
tMaratoneta v[]={
{"Verdi", {1,10,23} },
{"Gialli", {0,59,1} },
{"Bianchi", {0,59,2} },
{"Barbagrossa", {1,1,21} },
{"Rossi", {1,10,2} }
}; // Array di strutture
oppure
tMaratoneta v[]={
"Verdi", {1,10,23},
"Gialli", {0,59,1},
"Bianchi", {0,59,2},
"Barbagrossa", {1,1,21},
"Rossi", {1,10,2}
};
/**************************************************************************
* Nome: lab4-maratonetaMigliore.c *
* Autore: Alessandro Saetti *
* Data: 6/4/10 *
**************************************************************************/
#include <stdio.h>
#include <stdlib.h> // richiesta da realloc() e system()
typedef struct
{
int h, m, s;
} tTempo;
typedef struct
{
char nome[20];
tTempo t;
} tMaratoneta, *tMaratonetaPtr;
/*
* Nome: inizializzaMaratoneti
* Scopo: Inizializza un vettore di maratoneti con dati acquisiti da tastiera
* Non ho limite relativamente al numero di atleti da registrare
* Input: int *n: contiene il nr di atleti letti
* Output: restituisce il puntatore al vettore di maratoneti
* allocato dinamicamente
*/
tMaratonetaPtr inizializzaMaratoneti(int *n)
{
tMaratonetaPtr p = NULL;
bool bastaAtleti;
printf("Digita i dati (0:0:0 per terminare):\n");
printf("--------------------------------------------------\n");
do
{
p = (tMaratonetaPtr)realloc(p, (*n + 1) * sizeof(tMaratoneta));
if (p == NULL) exit(0); // L'allocazione è fallita
printf("Tempo (hh:mm:ss): ");
scanf("%d:%d:%d", &p[*n].t.h, &p[*n].t.m, &p[*n].t.s);
bastaAtleti=( (p[*n].t.h==0) && (p[*n].t.m==0) && (p[*n].t.s==0) );
if (!bastaAtleti)
{
printf("Nome: ");
scanf("%s", p[*n].nome);
*n = *n + 1;
}
printf("\n");
}
while (!bastaAtleti);
printf("--------------------------------------------------\n");
return p;
}
/*
* Nome: visualizzaMaratoneti
* Scopo: Visualizza un vettore di n maratoneti
* Input: Maratoneta v[]: un riferimento al vettore da visualizzare
* int n: la lunghezza del vettore
* Output: -
*/
void visualizzaMaratoneti(tMaratoneta v[], int n)
{
int i;
printf("Elenco atleti:\n");
for (i = 0; i < n; i++)
printf("%-20s %02d:%02d:%02d\n", v[i].nome, v[i].t.h, v[i].t.m, v[i].t.s);
printf("\n");
}
/*
* Nome: Minore
* Scopo: Individuare se un tempo cronometrato e' inferiore ad un altro
* Input: Tempo t1: primo tempo cronometrato
* Tempo t2: secondo tempo cronometrato
* Output: 1 se il primo tempo e' inferiore al secondo e 0 altrimenti
*/
bool Minore(tTempo t1, tTempo t2)
{
if (t1.h < t2.h)
return true;
else if (t1.h == t2.h)
{
if (t1.m < t2.m)
return true;
else if(t1.m == t2.m && t1.s < t2.s)
return true;
}
// Se arrivo qui t1 non è minore di t2
return false;
}
/*
* Nome: main
* Scopo: Inizializza e visualizza i dati contenuti in un vettore di maratoneti
* allocato dinamicamnte ed infine visualizza il maratoneta con il tempo
* inferiore
* Input: --
* Output: 0 se il programma termina correttamente
*/
int main()
{
int n = 0, i;
// Inizializzo la variabile best
tMaratoneta best = {"", {100, 60, 60}};
//INPUT
tMaratoneta *v = inizializzaMaratoneti(&n);
/* Potevo inizializzare l'array in questo modo:
tMaratoneta v[]={
{"Verdi", {1,10,23} },
{"Gialli", {0,59,1} },
{"Bianchi", {0,59,2} },
{"Barbagrossa", {1,1,21} },
{"Rossi", {1,10,2} }
};
*/
// ALGORITMO
for (i = 0; i < n ; i++)
if (Minore(v[i].t, best.t))
best = v[i];
//OUTPUT
visualizzaMaratoneti(v, n);
printf("Miglior maratoneta:\n%-20s %02d:%02d:%02d\n", best.nome, best.t.h, best.t.m, best.t.s);
system("pause");
return 0;
}
|
Costruire un programma che legge il nome e il tempo realizzato da ogni atleta di una sequenza di maratoneti.
Al termine mostra la classifica di arrivo partendo dall'atleta migliore. La sequenza termina quando si digita 0:0:0
|
|
SOLUZIONE:
OSSERVAZIONI:
1) In C++ (come in C) esistono i puntatori a funzione! Questi servono quando il programma deve scegliere quale funzione chiamare
fra diverse possibili, e la scelta non é definita a priori ma dipende dai dati del programma stesso. Per approfondimenti clicca
qui.
2) L'algoritmo di ordinamento utilizzato è l'Insertion Sort
3) Per le prove usa il seguente elenco di parole (cut & paste).
1:10:23
Verdi
0:59:1
Gialli
0:59:2
Bianchi
1:1:21
Barbagrossa
1:10:2
Rossi
0:0:0
/**************************************************************************
* Nome: casa1-maratonetiOrdinati.c *
* Autore: Alessandro Saetti *
* Data: 6/4/10 *
**************************************************************************/
#include <stdio.h>
#include <stdlib.h> // richiesto da system()
typedef struct
{
int h, m, s;
} tTempo;
typedef struct
{
char nome[20];
tTempo t;
} tMaratoneta, *tMaratonetaPtr;
/*
* Nome: inizializzaMaratoneti
* Scopo: Inizializza un vettore di maratoneti con dati acquisiti da tastiera
* Non ho limite relativamente al numero di atleti da registrare
* Input: int *n: contiene il nr di atleti letti
* Output: restituisce il puntatore al vettore di maratoneti
* allocato dinamicamente
*/
tMaratonetaPtr inizializzaMaratoneti(int *n)
{
tMaratonetaPtr p = NULL;
bool bastaAtleti;
printf("Digita i dati (0:0:0 per terminare):\n");
printf("--------------------------------------------------\n");
do
{
p = (tMaratonetaPtr)realloc(p, (*n + 1) * sizeof(tMaratoneta));
if (p == NULL) exit(0); // L'allocazione è fallita
printf("Tempo (hh:mm:ss): ");
scanf("%d:%d:%d", &p[*n].t.h, &p[*n].t.m, &p[*n].t.s);
bastaAtleti=( (p[*n].t.h==0) && (p[*n].t.m==0) && (p[*n].t.s==0) );
if (!bastaAtleti)
{
printf("Nome: ");
scanf("%s", p[*n].nome);
*n = *n + 1;
}
printf("\n");
}
while (!bastaAtleti);
printf("--------------------------------------------------\n");
return p;
}
/*
* Nome: visualizzaMaratoneti
* Scopo: Visualizza un vettore di n maratoneti
* Input: Maratoneta v[]: un riferimento al vettore da visualizzare
* int n: la lunghezza del vettore
* Output: -
*/
void visualizzaMaratoneti(tMaratoneta v[], int n)
{
int i;
printf("Elenco atleti:\n");
for (i = 0; i < n; i++)
printf("%-20s %02d:%02d:%02d\n", v[i].nome, v[i].t.h, v[i].t.m, v[i].t.s);
printf("\n");
}
/*
* Nome: Minore
* Scopo: Individuare se un tempo cronometrato e' inferiore ad un altro
* Input: Tempo t1: primo tempo cronometrato
* Tempo t2: secondo tempo cronometrato
* Output: 1 se il primo tempo e' inferiore al secondo e 0 altrimenti
*/
bool Minore(tTempo t1, tTempo t2)
{
if (t1.h < t2.h)
return true;
else if (t1.h == t2.h)
{
if (t1.m < t2.m)
return true;
else if(t1.m == t2.m && t1.s < t2.s)
return true;
}
// Se arrivo qui t1 non è minore di t2
return false;
}
/*
* Nome: ordina
* Scopo: Ordina i maratoneti in un database
* Input: Maratoneta v[]: il database di maratoneti
* int n: il numero di maratoneti contenuti nel database v
* Output: -
*/
void ordina(tMaratoneta v[], int n, bool (*confronta)(tTempo, tTempo))
{
int i, j;
tMaratoneta x;
for (i = 1; i < n; i++)
{
x = v[i];
for (j = i - 1; j >= 0 && (*confronta)(x.t, v[j].t) > 0; j--)
v[j+1] = v[j];
v[j+1] = x;
}
}
/*
* Nome: main
* Scopo: Inizializza e visualizza i dati contenuti in un vettore di maratoneti
* allocato dinamicamnte ed infine ordina la sequenza di maratoneti
* Input: --
* Output: 0 se il programma termina correttamente
*/
int main()
{
tMaratoneta *v = NULL;
int n = 0;
//INPUT
v = inizializzaMaratoneti(&n);
// ALGORITMO
ordina(v, n, Minore);
//OUTPUT
visualizzaMaratoneti(v, n);
system("pause");
return 0;
}
|
Costruire un programma che legge il nome e il tempo realizzato da ogni atleta di una sequenza di maratoneti.
Al termine mostra la classifica di arrivo in ordine crescente o decrescente a seconda della scelta dell'utente.
La sequenza termina quando si digita 0:0:0
|
|
SOLUZIONE:
OSSERVAZIONI:
1) Per approfondimenti relativamente agli array
di puntatori a funzione clicca qui.
2) Per approfondimenti relativamente al passaggio di puntatori a funzioni come argomenti di una funzione clicca
qui.
3) L'algoritmo di ordinamento utilizzato è l'Insertion Sort
4) Per le prove usa il seguente elenco di parole (cut & paste).
1:10:23
Verdi
0:59:1
Gialli
0:59:2
Bianchi
1:1:21
Barbagrossa
1:10:2
Rossi
0:0:0
/**************************************************************************
* Nome: casa2-maratonetiOrdinati.c *
* Autore: Alessandro Saetti *
* Data: 6/4/10 *
**************************************************************************/
#include <stdio.h>
#include <stdlib.h> // richiesto da system() e realloc()
typedef struct
{
int h, m, s;
} tTempo;
typedef struct
{
char nome[20];
tTempo t;
} tMaratoneta, *tMaratonetaPtr;
/*
* Nome: inizializzaMaratoneti
* Scopo: Inizializza un vettore di maratoneti con dati acquisiti da tastiera
* Non ho limite relativamente al numero di atleti da registrare
* Input: int *n: contiene il nr di atleti letti
* Output: restituisce il puntatore al vettore di maratoneti
* allocato dinamicamente
*/
tMaratonetaPtr inizializzaMaratoneti(int *n)
{
tMaratonetaPtr p = NULL;
bool bastaAtleti;
printf("Digita i dati (0:0:0 per terminare):\n");
printf("--------------------------------------------------\n");
do
{
p = (tMaratonetaPtr)realloc(p, (*n + 1) * sizeof(tMaratoneta));
if (p == NULL) exit(0); // L'allocazione è fallita
printf("Tempo (hh:mm:ss): ");
scanf("%d:%d:%d", &p[*n].t.h, &p[*n].t.m, &p[*n].t.s);
bastaAtleti=( (p[*n].t.h==0) && (p[*n].t.m==0) && (p[*n].t.s==0) );
if (!bastaAtleti)
{
printf("Nome: ");
scanf("%s", p[*n].nome);
*n = *n + 1;
}
printf("\n");
}
while (!bastaAtleti);
printf("--------------------------------------------------\n");
return p;
}
/*
* Nome: visualizzaMaratoneti
* Scopo: Visualizza un vettore di n maratoneti
* Input: Maratoneta v[]: un riferimento al vettore da visualizzare
* int n: la lunghezza del vettore
* Output: -
*/
void visualizzaMaratoneti(tMaratoneta v[], int n)
{
int i;
printf("Elenco atleti:\n");
for (i = 0; i < n; i++)
printf("%-20s %02d:%02d:%02d\n", v[i].nome, v[i].t.h, v[i].t.m, v[i].t.s);
printf("\n");
}
/*
* Nome: Minore
* Scopo: Individuare se un tempo cronometrato e' inferiore ad un altro
* Input: Tempo t1: primo tempo cronometrato
* Tempo t2: secondo tempo cronometrato
* Output: 1 se il primo tempo e' inferiore al secondo e 0 altrimenti
*/
bool Minore(tTempo t1, tTempo t2)
{
if (t1.h < t2.h)
return true;
else if (t1.h == t2.h)
{
if (t1.m < t2.m)
return true;
else if(t1.m == t2.m && t1.s < t2.s)
return true;
}
// Se arrivo qui t1 non è minore di t2
return false;
}
/*
* Nome: maggiore
* Scopo: Individuare se un tempo cronometrato e' superiore ad un altro
* Input: Tempo t1: primo tempo cronometrato
* Tempo t2: secondo tempo cronometrato
* Output: 1 se il primo tempo e' superiore al secondo e 0 altrimenti
*/
bool Maggiore(tTempo t1, tTempo t2)
{
if (t1.h==t2.h && t1.h==t2.h && t1.h==t2.h) return false;
return !(Minore(t1,t2));
}
/*
* Nome: ordina
* Scopo: Ordina i maratoneti in un database
* Input: Maratoneta v[]: il database di maratoneti
* int n: il numero di maratoneti contenuti nel database v
* Output: -
*/
void ordina(tMaratoneta v[], int n, bool (*confronta)(tTempo, tTempo))
{
int i, j;
tMaratoneta x;
for (i = 1; i < n; i++)
{
x = v[i];
for (j = i - 1; j >= 0 && (*confronta)(x.t, v[j].t) > 0; j--)
v[j+1] = v[j];
v[j+1] = x;
}
}
/*
* Nome: main
* Scopo: Inizializza e visualizza i dati contenuti in un vettore di maratoneti
* allocato dinamicamnte ed infine ordina la sequenza di maratoneti
* Input: --
* Output: 0 se il programma termina correttamente
*/
int main()
{
tMaratoneta *v = NULL;
int n = 0, TipoOrdinamento;
bool (*cmp[2])(tTempo, tTempo) = { Minore, Maggiore };
//INPUT
v = inizializzaMaratoneti(&n);
printf("Stampa in ordine crescente (0) o decrescente (1) -> ");
scanf("%d", &TipoOrdinamento);
// ALGORITMO
ordina(v, n , cmp[TipoOrdinamento]);
// Potevo anche usare l'operatore di dereferenziazione
//ordina(v, n , *cmp[TipoOrdinamento]);
//OUTPUT
visualizzaMaratoneti(v, n);
system("pause");
return 0;
}
SOLUZIONE:
Soluzione C++
Soluzione JavaScript
Soluzione VBA
Soluzione ASP
Soluzione PHP