L’area
di memoria e’ suddivisa in 4 aree:
a) area del codice
b) area dati globali
c) area heap
d) area stack
La prima contiene il codice del programma , la seconda contiene le variabili globali e statiche, l’ area heap e’ disponibile per allocazioni dinamiche mentre l’area stack contiene I record di attivazione delle funzioni (variabili locali e parametri).
.…. |
CODE SEGMENT |
DATA SEGMENT |
HEAP ¯ |
^ STACK |
….. |
Dunque
lo STACK contiene i record di attivazione delle funzioni che contiene tutto
quello che caratterizza l’esistere della funzione; ossia contiene:
a) Variabili locali
b) I parametri che ha
ricevuto
c) L’ indirizzo del
chiamante, dove deve ritornare alla fine della sua esecuzione
d) Un riferimento al
record di attivazione del chiamante.
La
dimensione del record non e’ fissa, in quanto ad ogni attivazione della
funzione si crea un nuovo record d’attivazione che e’ peculiare per quella
chiamata.
Precisiamo
meglio quest’ultima affermazione dicendo che la dimensione di tali record varia
da una funzione ad un’altra funzione ma per una medesima funzione, e’ fissa e
si puo’ calcolare a priori.
Il
record viene creato dinamicamente nello stesso tempo in cui viene chiamata la
funzione e tale record rimane nell’area di STACK finche’ la funzione e’ in
esecuzione.
La
memoria, occupata dal record nello stack, viene liberata quando la funzione
termina la sua esecuzione.
Le
funzioni che chiamano altre funzioni generano una sequenza di record di
attivazione i quali vengono allocati con lo stesso ordine delle chiamate e
deallocati (memoria liberata) in ordine inverso rispetto alle chiamate.
Quando
una funzione termina la sua esecuzione restituisce il controllo al CHIAMANTE ,
il quale riprende la propria esecuzione nel punto della istruzione che segue la
CHIAMATA alla funzione.
Si
intuisce che affinche’ il tutto funzioni in modo corretto e’ necessario che il
CHIAMANTE debba avere inserito (tramite istruzioni del S.O.) nel record di
attivazione della funzione l’indirizzo di ritorno, cioe’ l’indirizzo della
prossima istruzione che deve essere eseguita, quando gli viene restituito il
controllo (al termine della funzione).
Viene
inserito nel record anche il LINK DINAMICO che e’ un collegamento al record di
attivazione del CHIAMANTE, in modo che al termine della funzione il tutto
prosegui in modo corretto.
La
sequenza dei link dinamici e’ detta catena dinamica (il cui significato e’
quello di rappresentare la storia delle attivazioni).
Attraverso
l’uso dei puntatori si puo’ effettuare l’allocazione dinamica della memoria; con
tale allocazione i programmi possono utilizzare la memoria del sistema, durante
la loro esecuzione.
Il
limite di memoria allocabile potrebbe essere quello della memoria fisica
disponibile nel computer.
Con
la allocazione statica della memoria si riserva la memoria che serve alle
variabili globali durante la
compilazione, e si utilizza lo STACK per memorizzare le variabili locali; vi sono dei programmi che richiedono necessariamente
la allocazione dinamica della memoria RAM.
Il
sistema di allocazione dinamica del linguaggio C gestisce la memoria nello HEAP,
ossia la parte di memoria libera che si trova fra l’inizio del programma
e la fine dello stack.
Le
funzioni essenziali, per la allocazione dinamica nel C sono, malloc() e free()
; attraverso l’operatore sizeof
si garantisce la portabilita’ del software creato.
La
funzione malloc() alloca memoria (riserva memoria) e la funzione free() libera tale memoria e la restituisce al
sistema operativo.
Il
prototipo della funzione malloc() e’ il
seguente:
void
*malloc(size_t numero_di_byte);
numero_di_byte
e’ il numero di byte di memoria che vogliamo allocare mentre size_t e’ un tipo
di dati definito nel file di libreria stdlib.h, generalmente e’ un unsigned
int.
La
funzione malloc() quindi acquisisce
come argomento, il numero di byte che devono essere allocati e restituisce un
puntatore di tipo void , che puo’ essere assegnato a qualsiasi tipo di
puntatore.
Tale
funzione quando viene invocata (con successo) in sostanza restituisce un puntatore al primo byte della
memoria ram, che e’ stata rintracciata e allocata, nello heap. Questa porzione
di memoria
quando viene allocata non e’ piu’ possibile allocarla con successive
allocazioni ossia tale memoria dopo la prima chiamata della funzione malloc()
non e’ piu’ disponibile per altre successive chiamate a malloc().
La
porzione di memoria allocata sara’ occupata fino al termine della esecuzione
del programma "almeno nei sistemi operativi con
un certo grado di sofisticazione" o fino a quando non viene usata
esplicitamente la funzione free() che la libera
.
Se
la regione di memoria HEAP non ha spazio sufficiente, chiamate malloc(),
restituiranno un errore di allocazione e la malloc() restituisce valore nullo.
Esempio
Con
le seguenti istruzione allochiamo 1KB di memoria nello HEAP:
char
*puntatore;
puntatore
= malloc
(1024); /* allocazione di 1024 byte*/
la
variabile puntatore puntera’ al primo byte, dei 1024 resi disponibili.
Comunque
come anticipato sopra e’ buona regola di programmazione utilizzare sempre
l’operatore sizeof per assicurare la portabilita’ del software .
Esempio
Allocazione
di 100 interi usando sizeof
int
*puntatore;
puntatore
= malloc
(100* sizeof ( int ));
per
evitare il problema che la malloc restituisca un puntatore di valore nullo si
utilizza sempre la :
int
*puntatore;
if ( ! (puntatore = malloc (100* sizeof ( int ))))
{
printf (“Memoria insufficiente\n”);
exit(1);
}
Il
cui significato e’ il seguente:
se
il valore restituito da malloc() ed assegnato alla variabile puntatore e’
nullo, ( il !
inverte la condizione della if , che diventa vera
), si
esce dal programma "oppure al posto della exit(1) ci puo’ essere una routine di
gestione dell’errore";
se
il valore restituito da malloc() ed assegnato alla variabile puntatore non e’
nullo non viene eseguito il blocco di codice fra parentesi graffe e nella
variabile puntatore ritroviamo l'indirizzo del primo byte della area di memoria riservata
per i 100 interi.
Il
puntatore restituito dalla malloc(..) e’ di tipo void; per cui se volessimo
evitare la ambiguita’ o messaggi di warning "si dovrebbe"
utilizzare davanti alla
malloc(..) l’operatore di cast, ossia un operatore che forza ad assumere il
tipo che noi desideriamo "nel linguaggio C il
casting non e' pero' necessario a differenza di altri linguaggi come il C++";
per cui se volessimo che il tipo restituito da malloc nell’esempio sopra sia un puntatore ad interi utilizziamo il seguente cast
(int *);
avremo quindi :
if ( ! (puntatore =
(int *) malloc (100* sizeof ( int )))) ( 1)
( 1)
Nota: sulla necessita’ di inserire l’operatore di cast vi sono delle
controversie,alcuni compilatori lo
necessitano altri danno errore se lo si utilizza la regola dovrebbe
essere che in C non lo si dovrebbe utilizzare.
Attraverso
la funzione free() viene deallocata la memoria.
Il
prototipo di tate funzione e’:
void free( void *puntatore);
Porre
attenzione anche nell’uso della funzione free().
Utilizzando
la chiamata free(puntatore) successivamente alla malloc vista nell’ultimo
esempio liberiamo la memoria riservata per i 100 interi.
Se
ci trovassimo di fronte al seguente problema:
scrivere
un programma che inizializza, un vettore di n interi positivi, usando un ciclo
for, con l’inserimento dentro il vettore soltanto dei numeri pari compresi fra
0 ed n-1, con n letto da input?
come bisogna fare per risolvere un tale problema e non sprecare piu’ memoria di quanto ne sia necessaria per memorizzare P.i.s.(n/2) interi in un array?
dove
P.i.s.(x) rappresenta la funzione parte intera superiore del numero x, infatti
la quantita' di numeri pari che ritroviamo negli interi compresi da 0 fino ad x
e' data da tale funzione.
proviamo a scrivere il programma:
#include<stdio.h>
main ( )
{
int n, i ;
int VET[n];
for (i=0;i<n;i++) {
scanf (“%d“,&
n);
if
( n > 0 )
if
((i%2) = = 0) /* se il numero i e’ pari */
VET[i]=i;
}
}
Ebbene questo programma non funziona, in quanto abbiamo dichiarato un array in cui
la dimensione non e’ nota a priori ed il compilatore va in crisi; quando anche,
fosse nota vi sarebbe uno spreco di memoria per tutti i numeri letti da
input che sono dispari, ma potremmo "in questo caso" risolvere il
problema ultimo, dichiarando n come y== P.i.s.(n/2); ( esempio per n=7 ed
n=8 abbiamo y==4 numeri pari 0,2,4,6).
Ergo
Per risolvere in modo corretto tale problema, vi e’ bisogno della allocazione dinamica degli array, quando la dimensione dell'array non e' nota a priori oppure per effettuare in generale una una allocazione esatta.
Come esercizio potreste implementare un programma che risolva il medesimo problema utilizzando i puntatori.
Vediamo un altro esempio
che
utilizza un array allocato dinamicamente per acquisire una stringa strin, da
input (tastiera) e stampa la stringa all’indietro.
#include <stdio.h>
#include<stdlib.h> /*contiene la funzione malloc()*/
#include<string.h>
main( )
{
char *strin;
register int i; /*se puo' memorizza in un registro la variabile i*/
strin = malloc(80 * sizeof ( char ));
/*alloca 80 byte "se un carattere richiede 1 byte come in genere avviene "
e restituisce un puntatore dello stesso tipo di strin
notate che non abbiamo fatto uso del cast (char *) davanti a malloc;
in C non e' richiesto, in C++ e' obbligatorio, se si compila un programma C
in modalita' C++ l'assenza di cast ci da errore */
if (! strin)
{
printf ( "\nRichiesta di memoria negata\n");
exit (1);
}
printf("\ninserite la stringa\n");
gets (strin); /* acquisizione stringa da input*/
for (i=strlen(strin)-1; i>=0; i--)
printf ("%c",strin[i]);
printf("\n");
free (strin); /* liberiamo la memoria allocata*/
}
Una
versione di questo programma , compilata con DJGPP, si trova al seguente link:
programmi in c allocazione dinamica degli array
Attenzione
all’uso accidentale di un puntatore nullo, questo provocherebbe il crash del
sistema.
Notiamo che abbiamo usato strin con indice, poiche’ qualunque puntatore puo’ utilizzare gli indici come se fosse un array.
Un esempio di utilizzazione degli array dinamici puo' essere vista nel seguente programma :
#include<stdio.h>
#include<stdlib.h>
/*programma che accetta n interi,con n e gli interi letti da input,
li registra su un array dinamico e li stampa*/
int leggi_n();
int *leggi_array(int n);
void stampa_array(int *punt, int n);
main()
{
int *punt_a;
int num_el;
num_el=leggi_n();
punt_a=leggi_array(num_el);
stampa_array(punt_a,num_el);
free(punt_a);
}
int leggi_n()
{
int n;
printf("\ninserite il numero di elementi dell'array\n");
scanf("%d",&n);
return n;
}
int *leggi_array(int n)
{
int i=0;
int *pArray;
int elemento;
printf("\ninserite gli elementi interi dell'array\n");
pArray=malloc(n*sizeof(int));
while(i<n)
{
scanf("%d",&elemento);
*(pArray+i)=elemento;
i++;
}
return pArray;
}
void stampa_array(int *punt, int n)
{
int i=0;
printf("\nEcco i %d elementi interi che abbiamo inserito nell'array dinamico\n",n);
for(i=0;i<n;i++)
printf("%d) %d ", i+1, *(punt+i));
}
L’utilizzo
di array a piu’ dimensioni si complica poiche’ dato che le dimensioni
dell’array non sono state definite nel programma, non e’ possibile indicizzare
un puntatore come array a piu’ dimensioni senza incorrere in un errore. Quello
che e’ possibile fare e’ utilizzare gli array dinamici multi-dimensionali,
passandone il puntatore come argomento di una funzione del tipo :
#include<stdio.h>
#include<stdlib.h>
void
tabella( int punt[5][10])
main()
{
int *punt;
punt = malloc (100* sizeof(int) );
if ( ! punt )
{
printf
(“\nRichesta di memoria fallita\n”);
exit(1);
}
tabella(punt);
/*puntatore passato come parametro*/
}
void
tabella ( int punt[5][10] ) /*qua il
compilatore pensa che punt sia un array*/
{
…..
punt[i][j]=….;
}
La
funzione tabella puo’ definire il valore estremo di tutti gli indici, tranne
quello piu a sinistra, consentendo poi la normale indicizzazione.
In
sostanza nel programma abbiamo definito un argomento della funzione tabella in
modo tale che corrisponda ad un array dalle dimensioni che desideriamo, e cosi’
costringiamo il compilatore C a gestire gli array dinamici multi-dimensionali.
La
funzione tabella ( ) dal punto di vista del compilatore accede ad un array vero
e proprio; soltanto che la memoria per tale array viene allocata con malloc()
anziche' in modo automatico con le normali dichiarazioni di array.
Gli
errori piu’ frequenti utilizzando i puntatori si possono considerare di 3 tipi:
1) Puntatore non
inizializzato
Ad esempio consideriamo il seguente programmino:
#include<stdio.h>
main ( )
{
int numero, *punt;
numero = 100;
*punt = numero;
}
Questo programma e’ sbagliato, in quanto in esso si assegna
il valore 100 ad una locazione di memoria non nota, senza dare un valore al
puntatore punt.
Occorre sempre accertarsi che tutti i puntatori siano
inizializzati ad un valore significativo, prima del loro uso.
La versione corretta del programma puo’ essere:
#include<stdio.h>
main ( )
{
int
numero, y, *punt;
numero = 100;
punt=&y; /*assegnazione a punt
dell’indirizzo della variabile y*/
..
}
2) utilizzo sbagliato dei
puntatori
ad esempio utilizzando il seguente programma:
#include<stdio.h>
main ( )
{
int numero, *punt;
numero = 100;
punt = numero;
printf(“\n%d”,*punt);
}
Questo programma non stampera’ il valore di numero, ma un
valore sconosciuto.
L’ istruzione punt = numero; e’ sbagliata; essa assegna il
valore 100 al puntatore punt, il quale dovrebbe contenere un indirizzo
significativo.
#include<stdio.h>
main ( )
{
int
numero, *punt;
numero = 100;
punt = №
/*a punt si assegna l’indirizzo di memoria della variabile numero
che contiene il valore 100*/
printf (“\n%d”,*punt); /*stampa del valore 100*/
}
3) Usando un puntatore dentro un ciclo non
puo’ essere inizializzato fuori dal ciclo
e quindi una sola volta ,se all’interno del ciclo viene utilizzata una
funzione che legge una variabile da input ad ogni iterazione, a cui facciamo
puntare il puntatore ;
l’inizializzazione va fatta dentro il ciclo.
Notiamo che questo tipo di errore se
commesso puo’ provocare gravi danni.
Esempio
/*Programma sbagliato*/
#include<stdio.h>
#include<stdlib.h>
main ( )
{
char *punt1;
char string1[51];
punt1= string1;
string1= “inizia”;
while(strcmp(string1, “concluso”))
{
gets (string1);
while (!punt1)
printf
(“\n%d”,
*punt1 ++);
}
}
Programma
corretto
#include<stdio.h>
#include<stdlib.h>
main ( )
{
char
*punt1;
char string1[51];
string1= “inizia”;
while
( strcmp (string1, “concluso”))
{
punt1= string1;
gets
(string1);
while
(!punt1)
printf
(“\n%d”, *punt1 ++);
}
}
Altro esempio di errori
#include<stdio.h>
#include<stdlib.h>
main ( )
{
int *p; double *q, punt; p = NULL; q = NULL; if (p == NULL)
{ ... } if (p == q)
{ ... } /* abbiamo un errore! anche se sia p che q valgono NULL, hanno tipi diversi */ punt = NULL; /* e' sbagliata! punt non e' un puntatore */
}
Esempi su funzioni aventi array come argomenti
Abbiamo
gia' visto nella lezione 15, che nel linguaggio C il passaggio dei
parametri come argomenti di una funzione avviene in genere per valore, ma nel
caso volessimo passare ad una funzione un array non potremmo certamente
passarlo per valore (2) in quanto il nome
dell’array senza gli indici coincide con un puntatore al primo elemento
dell’array.
(2) nota Con qualche trucco e’ anche
possibile farlo
In sostanza nel passaggio per valore,
quando si chiama una funzione con argomento un array, non passiamo la copia di
tutto l’array ma soltanto l’indirizzo del primo elemento per cui in questo caso
gli array vengono passati alla funzione per indirizzo.
Nella chiamata per indirizzo ricordiamo
che la funzione puo’ modificare i valori degli elementi inclusi nell’array originale del chiamante. Infatti, conoscendo
la funzione, l’indirizzo in cui l’array e’ memorizzato , se all’interno del
corpo della funzione si effettuano delle modifiche agli elementi dell’array,
essa stara’ modificando effettivamente quelli del chiamante , nelle loro
locazioni di memoria originarie.
Esaminiamo adesso tre semplici esempi di
programmi :
1)
#include<stdio.h>
void print_array(int []
); /*prototipo funzione
print_array*/
main ( )
{
int
array[20], k;
for
(k=0; k<20; k++)
array[k]=k;
print_array (array);
/*chiamata
della funzione print_array con parametro attuale array*/
}
void
print_array (int numero[20])
{
int
j;
for
(j=0; j<20; j++)
printf (“%d”, numero[j]);
}
Notiamo che anche se il programma
dichiara numero come un array di 20 elementi interi, il compilatore C lo
converte in un puntatore intero, poiche’ non puo’ essere che un parametro
formale riceva un array completo.
2)
Il secondo programma di cui scriviamo
soltanto la funzione print_array , consiste nel dichiarare come parametro
formale un array non dimensionato
void
print_array(int numero[])
{
int
j;
for
(j=0;j<20; j++)
printf (“%d”, numero[j]);
}
numero risulta essere un puntatore di
tipo intero.
3)
Un modo piu’ professionale e’ quello di
dichiarare direttamente l’argomento della funzione print_array come puntatore:
void
print_array(int *numero)
{
int
j;
for
(j=0;j<20; j++)
printf (“%d”, numero[j]);
}
La conclusione a cui si perviene
analizzando questi 3 esempi e che il puntatore puo’ essere impiegato come un
array dal momento che e’ possibile usarlo congiuntamente agli indici.
La funzione realloc()
La funzione realloc() ha il seguente prototipo:
void *realloc(void *punt, size_t newsize);
essa modifica la dimensione dell'area di memoria puntata da punt portandola al valore newsize; newsize la dimensione a cui si vuole portare il blocco di memoria puo' essere maggiore o minore della dimensione che aveva in origine tale blocco.
Se newsize e' minore della dimensione attuale del blocco,viene scartata la parte finale "in eccedenza" del blocco.
Se newsize e' 0 e punt e' !=NULL , si avra' un comportamento simile alla free(punt).
Se punt e' NULL realloc() alloca newsize byte di memoria e ne restituisce il puntatore.
Se non puo' trovare memoria bastante per allocare newsize byte la funzione ritorna un puntatore NULL e l'area originale viene lasciata inalterata.
Allocazione esatta di stringhe ed array di stringhe
Attraverso le funzioni di allocazione dinamica "che abbiamo visto" ed i puntatori possiamo trattare le stringhe in modo tale da allocare per esse l'esatto quantitativo di memoria, necessario a contenerle.
Generalmente nella allocazione esatta di stringhe si utilizza un array (buffer di appoggio) dove inserire temporaneamente le stringhe lette da input, dimensionato per la stringa che si pensa essere quella piu' lunga da leggere.
Con il buffer di appoggio si ha un solo array ma si possono avere array dinamici (delle esatte dimensione) quanti se ne vogliono.
Un
puntatore a funzione, (e’ uno degli elementi piu’ complicati del linguaggio
C), e’ essenzialmente un nuovo tipo di
dato.
Nonostante
una funzione non sia una variabile, essa e’ individuata ugualmente da un
indirizzo, (precisamente dall’indirizzo di inizio del codice della funzione)
che puo’ essere benissimo assegnato ad un puntatore.
Quindi
il nome della funzione e’ in realta’ l’indirizzo di memoria dal quale inizia il
codice della funzione.
Mediante
l’uso di un puntatore a funzione, si puo’ passare una funzione come argomento
di un’altra funzione.
O
meglio un puntatore a funzione puo’ essere passato e restituito dalle funzioni,
puo’ essere immagazzinato in vettori e puo’ essere assegnato ad altri puntatori
a funzioni.
Possiamo
ottenere il puntatore ad un funzione utilizzandone il nome, senza parentesi
tonde ed eventuali argomenti, in maniera simile a come abbiamo visto per gli
array.
/*programma che confronta due stringhe per
verificarne l'uguaglianza*/
#include<stdio.h>
#include<string.h>
void
verifica_stringhe(char *punt_a, char *punt_b, int(*cmp)() );
main( )
{
char s1[81],s2[81];
int (*punt_funz)();
char car;
punt_funz=strcmp;
/*punt_funz e' il puntatore alla funzione di
libreria
strcmp string compare*/
printf("\nInserite
le due stringhe da confrontare\n");
gets(s1); /*lettura da input della stringa s1*/
gets(s2); /*lettura da input della stringa s2*/
verifica_stringhe(s1,s2,punt_funz);
printf("\nInserite
un carattere qualunque diverso da / per uscire\n");
scanf("%c",&car);
if(car!='/') exit();
}
void
verifica_stringhe(char *punt_a, char *punt_b, int(*cmp)() )
{
printf
("\nVerifica dell'uguaglianza fra stringhe\n");
if (!(*cmp)(punt_a,punt_b))
printf ("\n
Stringhe Uguali\n");
else
printf ("\nLe due
stringhe sono diverse\n");
}
Possiamo
notare che alla funzione verifica_stringhe vengono passati come argomenti due
puntatori a carattere, punt_a e punt_b, ed un puntatore a funzione.
Nel main la funzione viene chiamata con parametri attuali, le due stringhe s1 ed s2, lette da input con l’istruzione gets, ed il puntatore a funzione punt_funz a cui viene assegnato l’indirizzo iniziale della funzione di libreria strcmp, resa nota al programma attraverso la direttiva per il preprocessore #include<string.h>.
Esempio
/*Questo e' un semplice esempio di programma completo che
fa uso dei puntatori a funzione:*/
#include <stdio.h>
int funzione_pippo1(int x,int y)
{
printf("Somma calcolata Funzione_Pippo1(%i,%i)\n",x,y);
return x+y;
}
int funzione_pippo2(int x,int y)
{
printf("Prodotto calcolato Funzione_Pippo2(%i,%i)\n",x,y);
return x*y;
}
int main()
{
char car;
int (*p)(int,int); /* Puntatore a funzione */
int res;
p=funzione_pippo1; /* Ora punta a funzione_pippo1() */
res=p(5,8);
printf("Risultato = %i\n",res);
p=funzione_pippo2; /* Ora punta a funzione_pippo2() */
res=p(5,8);
printf("Risultato = %i\n",res);
scanf("\n%c",&car);
return 0;
}
Esamineremo
adesso un programma utilizzante un vettore di puntatori a funzioni.
Definiamo
innanzitutto 3 funzioni: funzione1, funzione2, funzione3 che riceveranno un
argomento intero e non restituiranno alcun valore (sono di tipo void).
I
puntatori che fanno riferimento a queste 3 funzioni vengono memorizzati nel
vettore funz, che viene cosi’ dichiarato:
void (*funz[3]) ( int ) = {
funzione1, funzione2, funzione3 };
Questa
dichiarazione ci indica un array di 3 puntatori a funzioni che riceveranno come argomento un int e che
restituiranno void; l’array e’ stato inizializzato con i nomi delle tre
funzioni.
#include<stdio.h>
void
funzione1( int );
void
funzione2( int );
void
funzione3( int );
main ( )
{
void
(*funz[3]) ( int ) = {funzione1,
funzione2, funzione3};
int scelta;
printf
(“\nInserite un numero compreso fra 0 e 2, 3 per finire:\n);
scanf
(“%d”,&scelta);
while
( scelta >=0 && scelta < 3)
{
( *funz[scelta] ) ( scelta );
printf
(“\nInserite un numero fra 0 e 2, 3 per finire: \n);
scanf
(“%d”,&scelta);
}
printf
(“\nHa inserito il 3 per concludere\n”);
return 0;
}
void
funzione1( int x)
{
printf
(“\nHa inserito %d affinche’ venisse chiamata funzione1\n”,x);
}
void
funzione2( int y)
{
printf
(“\nHa inserito %d affinche’ venisse chiamata funzione2\n”,y);
}
void
funzione3( int z)
{
printf
(“\nHa inserito %d affinche’ venisse chiamata funzione3\n”,z);
}
Nel
main la chiamata della funzione avviene attraverso la seguente istruzione:
(
*funz[scelta]) ( scelta )
funz[scelta]
seleziona il puntatore immagazzinato nella posizione generica del vettore (
scelta).
Se
scelta vale 1 verra’ selezionato il puntatore a funzione2. Poi la funzione
viene chiamata ed il valore che contiene scelta gli viene fornito come
parametro attuale.
Le
3 funzioni una volta chiamate si limitano a visualizzare il valore
dell’argomento ricevuto ed il proprio nome di funzione, affinche’ possiamo
verificare che sia stata chiamata in modo corretto.