Lezione XVIII                                           frames      noframes

Allocazione dinamica e puntatori a funzioni

Breve Introduzione alla macchina virtuale del C

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).

Le funzioni di allocazione dinamica del linguaggio C

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.

Allocazione dinamica degli array

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

}

 

 

esercizio

 

esercizio

 

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.

Errori frequenti usando i puntatori

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.

Versione corretta del programma :

#include<stdio.h>

main ( )

{

 int  numero, *punt;

 numero = 100;

 punt = &numero;

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

 

 

esercizio

 

 

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. 

 

 

esercizio 

 

esercizio 

 

I puntatori a funzioni

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.

Esempio di utilizzo dei puntatori a funzione

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

 

Esempio di array di puntatori  a funzioni

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.

 


 

esercizio

 

esercizio

 

Test 18