SlideShare ist ein Scribd-Unternehmen logo
1 von 52
Downloaden Sie, um offline zu lesen
Funzioni in C
Modello cliente/servitore
Servitore:
• Un qualunque ente computazionale capace di nascondere la
  propria organizzazione interna
• presentando ai clienti una precisa interfaccia per lo scambio
  di informazioni

Cliente:
• qualunque ente in grado di invocare uno o più servitori per
  svolgere il proprio compito
Modello cliente/servitore
Un servitore può
• essere passivo o
attivo



• servire molti clienti oppure costituire la risorsa privata di
  uno specifico cliente
   – in particolare: può servire un cliente alla volta, in
     sequenza, oppure più clienti per volta, in parallelo

• trasformarsi a sua volta in cliente, invocando altri
   servitori o anche se stesso.
Comunicazione cliente/servitore

•Lo scambio di informazioni tra un cliente e un servitore
può avvenire

  in modo esplicito tramite le interfacce stabilite dal
servitore

   in modo implicito tramite aree-dati accessibili ad
entrambi,
    ossia l’ambiente condiviso
FUNZIONI COME SERVITORI

• Una funzione è un servitore
    – passivo
    – che serve un cliente per volta
    – che può trasformarsi in cliente invocando altre funzioni o se
      stessa
• Una funzione è un servitore dotato di nome che incapsula le
   istruzioni che realizzano un certo servizio.
• Il cliente chiede al servitore di svolgere il servizio
     – chiamando tale servitore (per nome)
     – fornendogli le necessarie informazioni
• Nel caso di una funzione, cliente e servitore comunicano
   mediante l’interfaccia della funzione.
Interfaccia di una funzione
•L’interfaccia (o prototipo) di una funzione comprende

             nome della funzione
            lista dei parametri
             tipo del valore da essa denotato

• Esplicita il contratto di servizio fra cliente e servitore

•Cliente e servitore comunicano quindi mediante
  i parametri trasmessi dal cliente al servitore all’atto della chiamata
   il valore restituito dal servitore al cliente
ESEMPIO
• Calcolo del massimo di due valori
                                                      3,
• Sequenza operazioni:                                5
    – Il cliente comunica al
       servitore i due valori
    – Il servitore calcola il massimo
    – Il servitore comunica al                        5
      cliente il valore del massimo

•Cliente e servitore devono essere d’accordo su:
    –il tipo dei valori                   int
    –quanti sono i valori                 2
    –qual’è il nome del servitore         max
    –qual’è il tipo del valore di ritorno int

•Il cliente non è interessato all’algoritmo che il servitore utilizza
ESEMPIO
• Quindi, dal lato del servitore      • Il cliente dovrà mandare al
   dovrà esserci scritto:                 servitore identificato da quel
– come si chiama                          nome
– di quanti dati ha bisogno e di           – i dati di ingresso, in numero
   che tipo                                  e tipo giusti
– di che tipo è il valore calcolato        – ricevere il risultato
Funzioni: esempio C

int max (int x, int y ){ /*interfaccia
if (x>y) return x ;
else return y;
}

• Il simbolo max denota il nome della funzione

• Le variabili intere x e y sono i parametri della funzione

• Il valore restituito è un intero int
Comunicazione cliente/servitore

• Il cliente passa informazioni al servitore mediante una serie di
   parametri


Parametri formali:
  sono specificati nella dichiarazione del servitore
  esplicitano il contratto fra servitore e cliente
  indicano che cosa il servitore si aspetta dal cliente

Parametri attuali:
  sono trasmessi dal cliente all’atto della chiamata
  devono corrispondere ai parametri formali in numero,
  posizione e tipo
Funzioni: esempio parametri
Comunicazione cliente/servitore

Legame tra parametri attuali e parametri
formali:
• effettuato al momento della chiamata, in
modo
  dinamico

Tale legame:
  vale solo per l’invocazione corrente
  vale solo per la durata della funzione
Esempio




          All’atto di questa
          chiamata della funzione,
          si effettua un legame tra:
          xez
          ye4
Esempio:




           All’atto della chiamata
           della funzione si effettua
           il legame tra:
            xe5
            yez
Information hiding
• La struttura interna (corpo) di una funzione è
  completamente inaccessibile dall’esterno

• Così facendo si garantisce protezione dell’informazione
  (information hiding)

• Una funzione è accessibile solo attraverso la sua
  interfaccia

• Quindi posso cambiare l’algoritmo della funzione senza
  preoccuparmi di quello che succede dal lato del cliente
Definizione di funzione in C

<tipoValore> <nome>(<parametri-formali>) {

     <corpo>
                                          La forma base è:
 }
                                          return <espressione>;


<parametri-formali>
    o una lista vuota: void
    o una lista di variabili (separate da virgole)
visibili solo entro il corpo della funzione

<tipoValore>
    deve coincidere con il tipo del valore restituito dalla funzione
    Può non esservi valore restituiti, in tal caso il tipo è void
Definizione di funzione in C

 <tipoValore> <nome>(<parametri-formali>) {

       <corpo>
                                            La forma base è:
   }
                                            return <espressione>;

• Nella parte corpo possono essere presenti:
  definizioni e/o dichiarazioni locali (parte dichiarazioni)
  e un insieme di istruzioni (parte istruzioni)

• I dati riferiti nel corpo possono essere costanti, variabili,
  oppure parametri formali

• All'interno del corpo, i parametri formali vengono trattati come variabili
Funzioni: nascita e morte

• All’atto della chiamata:
  l’esecuzione del cliente viene sospesa e il
  controllo passa al servitore

• Il servitore “vive” solo per il tempo necessario a
  svolgere il servizio

• Al termine, il servitore “muore”, e l’esecuzione
  torna al cliente
Chiamata di funzione
• La chiamata di funzione è un’espressione della forma

      <nomefunzione> ( <parametri-attuali> )

• dove:

      <parametri-attuali> ::=
           [ <espressione> ] { , <espressione> }
Funzioni: esempio
         Parametri formali


int max (int x, int y ){
                                    SERVITORE
    if (x>y) return x ;
         else return y;             Definizione
}
                                    della funzione




  main() {
                                    CLIENTE
             int z = 8;
             int m;                 Chiamata
             m = max ( z, 4);
   }
                                    della funzione

                Parametri attuali
Risultato di una funzione
• L’istruzione return provoca la restituzione del
  controllo al cliente, unitamente al valore della
  espressione che la segue.

• Eventuali istruzioni successive alla return non
  saranno mai eseguite


      int max (int x, int y ){
            if (x>y) return x ;
            else return y;
      }
Funzioni: esempio
         Parametri formali


int max (int x, int y ){
                                SERVITORE
    if (x>y) return x ;
         else return y;         Definizione
}
                                della funzione




  main() {
                                CLIENTE
             int z = 8;
             int m;             Chiamata
             m = max ( z, 4);
   }
                                della funzione

                  Risultato
Riassumendo
All’atto dell’invocazione di una funzione:

• si crea una nuova attivazione (istanza) del servitore

• si alloca la memoria per i parametri
  (e le eventuali variabili locali)

• si trasferiscono i parametri al servitore

• si trasferisce il controllo al servitore

• si esegue il codice della funzione
Passaggio dei parametri
In generale, un parametro può essere trasferito dal cliente al servitore:


 • per valore o copia (by value)
        si trasferisce il valore del parametro attuale


 • per riferimento (by reference)
       si trasferisce un riferimento al parametro attuale
Passaggio per valore
• Si trasferisce una copia del valore del parametro attuale




         cliente
Passaggio per valore
• Si trasferisce una copia del valore del parametro attuale
                                                              Valore
                                                           (copiato) di z


                          copia


                                                                 Ogni azione
                                                                 fatta su w è
                                                                 locale al
                                                                 servitore
                                             Istanza del
                                              servitore

   cliente                               servitore
Passaggio per riferimento
• Si trasferisce un riferimento al parametro attuale




          cliente
Passaggio per valore
• Si trasferisce un riferimento al parametro attuale
                                               Riferimento a z
                                                  (indirizzo)


                    riferimento
                x
                                         x
                                                        Ogni azione
                                                        fatta su w è
                                                        In realtà
                                                        fatta sulla
                                      Istanza del
                                                        variabile z
                                       servitore
                                                        del cliente


      cliente                        servitore
Passaggio dei parametri in C

• In C, i parametri sono trasferiti sempre e solo per valore


    si trasferisce una copia del parametro attuale, non l’originale

    tale copia è strettamente privata e locale a quel servitore

    il servitore potrebbe quindi alterare il valore ricevuto, senza
    che ciò abbia alcun impatto sul cliente
Passaggio dei parametri in C

• In C, i parametri sono trasferiti sempre e solo per valore


      Conseguenza:

      • è impossibile usare un parametro per trasferire informazioni
        verso il cliente

      • per trasferire un’informazione al cliente si sfrutta il valore di
        ritorno della funzione
Esempio

Perché il passaggio per valore non basta?

Problema:
   scrivere una procedura che scambi i valori di due variabili intere

Specifica:
   Dette A e B le due variabili, ci si può appoggiare a una variabile
   ausiliaria T, e svolgere lo scambio in tre fasi

Frammento di codice:
      int a,b,t;
      ...
      t = a; a = b; b = t;
      …
Esempio
• Supponendo di utilizzare, senza preoccuparsi, il passaggio per
  valore usato finora, la codifica potrebbe essere espressa come
  segue:


       void scambia(int a, int b) {
          int t;
          t = a; a = b; b = t;
          return;
       }
Esempio
Il cliente invocherebbe quindi la procedura così:

        main(){
          int y = 5, x = 33;
          scambia(x, y);
           /* ora dovrebbe essere
                x=5, y=33 ...
              MA NON È VERO
           */
        }


                Perché non funziona?
Esempio: cosa è successo?

• La procedura ha effettivamente scambiato i valori di A e B al suo interno

• ma questa modifica non si è propagata al cliente, perché sono state
  scambiate le copie locali alla procedura, non gli originali

• al termine della procedura, le sue variabili locali sono state distrutte,
  quindi nulla è rimasto del lavoro svolto dalla procedura
Esempio: cosa è successo?
• Ogni azione fatta su a e b è strettamente locale al servitore.
  Quindi a e b vengono scambiati, ma quando il servitore termina,
  tutto scompare
                                                       Valori (copiati)
                                                          di a e b


                              copia
      x


      y



          cliente                          servitore
Passaggio dei parametri in C
    Il C adotta sempre il passaggio per valore!

È sicuro: le variabili del cliente e del servitore sono disaccoppiate

... ma non consente di scrivere componenti software il cui scopo
sia diverso dal calcolo di una espressione

Per superare questo limite occorre il passaggio per riferimento
Passaggio per riferimento

Il passaggio per riferimento (by reference):

• non trasferisce una copia del valore del parametro attuale

• ma un riferimento al parametro, in modo da dare al servitore
  accesso diretto al parametro in possesso del cliente

• il servitore, quindi, accede direttamente al dato del cliente e
   può modificarlo
Passaggio per riferimento

Si trasferisce un riferimento ai parametri attuali (cioè i loro indirizzi)




      x


      y



          cliente
Passaggio per riferimento
        Ogni azione fatta su a e b, in realtà
         è fatta su x e y nell’environment del
        cliente
                                                      Riferimenti a
                                                     x e y (indirizzi)


               α      riferimento
x

               β     riferimento
y



    cliente                              servitore
Passaggio per riferimento
       quindi, scambiando α e β, in
       realtà si scambiano x e y

                                                   Riferimenti a
                                                  x e y (indirizzi)


               α     riferimento
x

               β     riferimento
y



    cliente                           servitore
Realizzare il passaggio per riferimento in C

• Il C non fornisce direttamente un modo per attivare il
  passaggio per riferimento -> a volte occorre costruirselo

• è una grave mancanza!

• il C lo fornisce indirettamente solo per alcuni tipi di dati

• quindi, occorre costruirselo quando serve.
  (vedremo più avanti dei casi)
Funzioni e strutture

• Con le funzioni si possono usare come parametri e
  come valore di ritorno le strutture (con gli array è un
  po’ diverso, come vedremo)

• L’utilizzo delle funzioni permette di costruire
  semplicemente applicazioni con la metodologia
  top-down

• Si scriva un programma che permette di
– leggere due frazioni
– calcolarne la somma
– visualizzare il risultato
Esempio
• Per prima cosa, definiamo le strutture dati:
          – una frazione è costituita da un numeratore ed un denominatore

typedef struct { int num; int den; } frazione;

• Poi scriviamo l’algoritmo partendo dalla versione più astratta.
  Scriviamo il main invocando le varie funzioni che ci servono

main()
{ frazione f1, f2, somma;
  f1 = leggiFrazione();
  f2 = leggiFrazione();
  somma = sum(f1,f2);
  printf("%d/%d",somma.num,somma.den);
}
Esempio


• Poi implementiamo le funzioni che abbiamo invocato nel main:


 frazione leggiFrazione()
 { frazione f;
    scanf("%d",&f.num);
    scanf("%d",&f.den);
    return f;
  }
Esempio
• La somma di due frazioni si calcola così:
– calcolo il minimo comun denominatore (minimo comune multiplo
  dei denominatori); questo è il denominatore della somma
– porto la prima frazione al comun denominatore
– porto la seconda frazione al comun denominatore
– calcolo la somma dei numeratori: questo è il numeratore della somma

frazione sum(frazione f1, frazione f2)
   { int mcd; // minimo comun denominatore
      frazione somma;
      mcd = mcm(f1.den,f2.den);
      somma.den = mcd;
      f1 = portaDen(f1,mcd);
      f2 = portaDen(f2,mcd);
      somma.num = f1.num + f2.num;
      return somma;
    }
Esempio
• Infine implementiamo le funzioni che abbiamo usato nelle funzioni

• Per portare una frazione ad un denominatore, devo moltiplicare
  numeratore e denominatore per la stessa quantità

           nuovoNum/nuovoDen = vecchioNum/vecchioDen

• quindi
           nuovoNum=vecchioNum*nuovoDen/vecchioDen

frazione portaDen(frazione f, int nDen)
{ frazione nuovo;
   nuovo.den = nDen;
   nuovo.num = f.num*nDen/f.den;
   return nuovo;
 }
Esempio

• Per calcolare il minimo comune multiplo di due
  interi, posso farne il prodotto e dividere per il
  massimo comun divisore dei due


int mcm(int a, int b)
  { return a*b/MCD(a,b);
   }
Esempio

• Per calcolare il MCD di due numeri, posso usare
  il metodo di Euclide

int MCD(int m, int n)
   { while (m != n)
         if (m>n)
               m=m-n;
         else n=n-m;
       return m;
     }
Il programma risultante
                                       frazione sum(frazione f1, frazione f2)
#include <stdio.h>                     {    int mcd;
typedef struct { int num; int den; }       frazione somma;
     frazione;                             mcd = mcm(f1.den,f2.den);
int MCD(int m, int n)                      somma.den = mcd;
{ while (m != n)                           f1 = portaDen(f1,mcd);
     if (m>n)                              f2 = portaDen(f2,mcd);
                                           somma.num = f1.num + f2.num;
           m=m-n;
                                           return somma;
     else n=n-m;
                                         }
   return m;
                                       frazione leggiFrazione()
}
                                       {     frazione f;
int mcm(int a, int b)                       scanf("%d",&f.num);
{ return a*b/MCD(a,b);                     scanf("%d",&f.den);
}                                          return f;
frazione portaDen(frazione f, int        }
     nDen)                             main()
{ frazione nuovo;                      {    frazione f1, f2, somma;
  nuovo.den = nDen;                        f1 = leggiFrazione();
  nuovo.num = f.num*nDen/f.den;            f2 = leggiFrazione();
   return nuovo;                           somma = sum(f1,f2);
}                                          printf("%d/%d",somma.num,somma.den)
                                           ;
                                         }
Il programma risultante
• E` abbastanza facile da scrivere e da capire
• E` facile da modificare
• Es: voglio assicurarmi che l’utente non inserisca una frazione che ha per
  denominatore zero
• Intervengo in una sola funzione: la leggiFrazione
     – è una funzione di 4 istruzioni, quindi facile da capire e da
       modificare

frazione leggiFrazione()
{ frazione f;
   do
   { scanf("%d",&f.num);
     scanf("%d",&f.den);
     if (f.den==0)
           printf(“Re-inserire la frazionen");
    } while (f.den == 0);
           return f;
 }
Modificabilità
• Ora voglio che mi fornisca solo frazioni ai minimi termini

• Aggiungo una funzione riduci. Posso invocarla nel main



main()
{   frazione f1, f2, somma;
         f1 = leggiFrazione();
         f2 = leggiFrazione();
         somma = riduci(sum(f1,f2));
         printf("%d/%d",somma.num,somma.den);
  }
Modificabilità

• Poi definisco la nuova funzione riduci

• Per ridurre una funzione ai minimi termini, basta dividere
  numeratore e denominatore per il loro MCD


  frazione riduci(frazione f)
  { int m = MCD(f.num,f.den);
     f.num = f.num/m;
     f.den = f.den/m;
     return f;
   }

Weitere ähnliche Inhalte

Mehr von STELITANO

Dispenza aloisi
Dispenza aloisiDispenza aloisi
Dispenza aloisiSTELITANO
 
Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)STELITANO
 
Lezione 22 (7 maggio 2012)
Lezione 22 (7 maggio 2012)Lezione 22 (7 maggio 2012)
Lezione 22 (7 maggio 2012)STELITANO
 
Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)STELITANO
 
Lezione 20 (2 maggio 2012) seconda parte
Lezione 20 (2 maggio 2012) seconda parteLezione 20 (2 maggio 2012) seconda parte
Lezione 20 (2 maggio 2012) seconda parteSTELITANO
 
Lezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parteLezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parteSTELITANO
 
Lezione 19 (18 aprile 2012) ricerca
Lezione 19 (18 aprile 2012)   ricercaLezione 19 (18 aprile 2012)   ricerca
Lezione 19 (18 aprile 2012) ricercaSTELITANO
 
Lezione 18 (18 aprile 2012) ordinamenti
Lezione 18 (18 aprile 2012)   ordinamentiLezione 18 (18 aprile 2012)   ordinamenti
Lezione 18 (18 aprile 2012) ordinamentiSTELITANO
 
Lezione 15 (2 aprile 2012)
Lezione 15 (2 aprile 2012)Lezione 15 (2 aprile 2012)
Lezione 15 (2 aprile 2012)STELITANO
 
Lezione 14 (2 aprile 2012)
Lezione 14 (2 aprile 2012)Lezione 14 (2 aprile 2012)
Lezione 14 (2 aprile 2012)STELITANO
 
Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)STELITANO
 
Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)STELITANO
 
Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)STELITANO
 
Lezione 12 (28 marzo 2012) puntatori vettori
Lezione 12 (28 marzo 2012) puntatori   vettoriLezione 12 (28 marzo 2012) puntatori   vettori
Lezione 12 (28 marzo 2012) puntatori vettoriSTELITANO
 
Lezione 12 (28 marzo 2012) funzioni memoria - puntatori
Lezione 12 (28 marzo 2012) funzioni   memoria - puntatoriLezione 12 (28 marzo 2012) funzioni   memoria - puntatori
Lezione 12 (28 marzo 2012) funzioni memoria - puntatoriSTELITANO
 
Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)STELITANO
 
Soluzione esonero del 4 aprile 2012
Soluzione esonero del 4 aprile 2012Soluzione esonero del 4 aprile 2012
Soluzione esonero del 4 aprile 2012STELITANO
 
Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)STELITANO
 
Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)STELITANO
 
Eserc v del 26 marzo 2012
 Eserc v del 26 marzo 2012 Eserc v del 26 marzo 2012
Eserc v del 26 marzo 2012STELITANO
 

Mehr von STELITANO (20)

Dispenza aloisi
Dispenza aloisiDispenza aloisi
Dispenza aloisi
 
Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)
 
Lezione 22 (7 maggio 2012)
Lezione 22 (7 maggio 2012)Lezione 22 (7 maggio 2012)
Lezione 22 (7 maggio 2012)
 
Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)Lezione 21 (2 maggio 2012)
Lezione 21 (2 maggio 2012)
 
Lezione 20 (2 maggio 2012) seconda parte
Lezione 20 (2 maggio 2012) seconda parteLezione 20 (2 maggio 2012) seconda parte
Lezione 20 (2 maggio 2012) seconda parte
 
Lezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parteLezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parte
 
Lezione 19 (18 aprile 2012) ricerca
Lezione 19 (18 aprile 2012)   ricercaLezione 19 (18 aprile 2012)   ricerca
Lezione 19 (18 aprile 2012) ricerca
 
Lezione 18 (18 aprile 2012) ordinamenti
Lezione 18 (18 aprile 2012)   ordinamentiLezione 18 (18 aprile 2012)   ordinamenti
Lezione 18 (18 aprile 2012) ordinamenti
 
Lezione 15 (2 aprile 2012)
Lezione 15 (2 aprile 2012)Lezione 15 (2 aprile 2012)
Lezione 15 (2 aprile 2012)
 
Lezione 14 (2 aprile 2012)
Lezione 14 (2 aprile 2012)Lezione 14 (2 aprile 2012)
Lezione 14 (2 aprile 2012)
 
Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)
 
Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)Lezione 16 (2 aprile 2012)
Lezione 16 (2 aprile 2012)
 
Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)Lezione 13 (2 aprile 2012)
Lezione 13 (2 aprile 2012)
 
Lezione 12 (28 marzo 2012) puntatori vettori
Lezione 12 (28 marzo 2012) puntatori   vettoriLezione 12 (28 marzo 2012) puntatori   vettori
Lezione 12 (28 marzo 2012) puntatori vettori
 
Lezione 12 (28 marzo 2012) funzioni memoria - puntatori
Lezione 12 (28 marzo 2012) funzioni   memoria - puntatoriLezione 12 (28 marzo 2012) funzioni   memoria - puntatori
Lezione 12 (28 marzo 2012) funzioni memoria - puntatori
 
Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)
 
Soluzione esonero del 4 aprile 2012
Soluzione esonero del 4 aprile 2012Soluzione esonero del 4 aprile 2012
Soluzione esonero del 4 aprile 2012
 
Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)Lezione 12 (28 marzo 2012)
Lezione 12 (28 marzo 2012)
 
Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)Lezione 11 (26 marzo 2012)
Lezione 11 (26 marzo 2012)
 
Eserc v del 26 marzo 2012
 Eserc v del 26 marzo 2012 Eserc v del 26 marzo 2012
Eserc v del 26 marzo 2012
 

Lezione 10 (21 marzo 2012)3

  • 2. Modello cliente/servitore Servitore: • Un qualunque ente computazionale capace di nascondere la propria organizzazione interna • presentando ai clienti una precisa interfaccia per lo scambio di informazioni Cliente: • qualunque ente in grado di invocare uno o più servitori per svolgere il proprio compito
  • 3. Modello cliente/servitore Un servitore può • essere passivo o attivo • servire molti clienti oppure costituire la risorsa privata di uno specifico cliente – in particolare: può servire un cliente alla volta, in sequenza, oppure più clienti per volta, in parallelo • trasformarsi a sua volta in cliente, invocando altri servitori o anche se stesso.
  • 4. Comunicazione cliente/servitore •Lo scambio di informazioni tra un cliente e un servitore può avvenire in modo esplicito tramite le interfacce stabilite dal servitore in modo implicito tramite aree-dati accessibili ad entrambi, ossia l’ambiente condiviso
  • 5. FUNZIONI COME SERVITORI • Una funzione è un servitore – passivo – che serve un cliente per volta – che può trasformarsi in cliente invocando altre funzioni o se stessa • Una funzione è un servitore dotato di nome che incapsula le istruzioni che realizzano un certo servizio. • Il cliente chiede al servitore di svolgere il servizio – chiamando tale servitore (per nome) – fornendogli le necessarie informazioni • Nel caso di una funzione, cliente e servitore comunicano mediante l’interfaccia della funzione.
  • 6. Interfaccia di una funzione •L’interfaccia (o prototipo) di una funzione comprende nome della funzione lista dei parametri tipo del valore da essa denotato • Esplicita il contratto di servizio fra cliente e servitore •Cliente e servitore comunicano quindi mediante i parametri trasmessi dal cliente al servitore all’atto della chiamata il valore restituito dal servitore al cliente
  • 7. ESEMPIO • Calcolo del massimo di due valori 3, • Sequenza operazioni: 5 – Il cliente comunica al servitore i due valori – Il servitore calcola il massimo – Il servitore comunica al 5 cliente il valore del massimo •Cliente e servitore devono essere d’accordo su: –il tipo dei valori int –quanti sono i valori 2 –qual’è il nome del servitore max –qual’è il tipo del valore di ritorno int •Il cliente non è interessato all’algoritmo che il servitore utilizza
  • 8. ESEMPIO • Quindi, dal lato del servitore • Il cliente dovrà mandare al dovrà esserci scritto: servitore identificato da quel – come si chiama nome – di quanti dati ha bisogno e di – i dati di ingresso, in numero che tipo e tipo giusti – di che tipo è il valore calcolato – ricevere il risultato
  • 9. Funzioni: esempio C int max (int x, int y ){ /*interfaccia if (x>y) return x ; else return y; } • Il simbolo max denota il nome della funzione • Le variabili intere x e y sono i parametri della funzione • Il valore restituito è un intero int
  • 10. Comunicazione cliente/servitore • Il cliente passa informazioni al servitore mediante una serie di parametri Parametri formali: sono specificati nella dichiarazione del servitore esplicitano il contratto fra servitore e cliente indicano che cosa il servitore si aspetta dal cliente Parametri attuali: sono trasmessi dal cliente all’atto della chiamata devono corrispondere ai parametri formali in numero, posizione e tipo
  • 12. Comunicazione cliente/servitore Legame tra parametri attuali e parametri formali: • effettuato al momento della chiamata, in modo dinamico Tale legame: vale solo per l’invocazione corrente vale solo per la durata della funzione
  • 13. Esempio All’atto di questa chiamata della funzione, si effettua un legame tra: xez ye4
  • 14. Esempio: All’atto della chiamata della funzione si effettua il legame tra: xe5 yez
  • 15. Information hiding • La struttura interna (corpo) di una funzione è completamente inaccessibile dall’esterno • Così facendo si garantisce protezione dell’informazione (information hiding) • Una funzione è accessibile solo attraverso la sua interfaccia • Quindi posso cambiare l’algoritmo della funzione senza preoccuparmi di quello che succede dal lato del cliente
  • 16. Definizione di funzione in C <tipoValore> <nome>(<parametri-formali>) { <corpo> La forma base è: } return <espressione>; <parametri-formali> o una lista vuota: void o una lista di variabili (separate da virgole) visibili solo entro il corpo della funzione <tipoValore> deve coincidere con il tipo del valore restituito dalla funzione Può non esservi valore restituiti, in tal caso il tipo è void
  • 17. Definizione di funzione in C <tipoValore> <nome>(<parametri-formali>) { <corpo> La forma base è: } return <espressione>; • Nella parte corpo possono essere presenti: definizioni e/o dichiarazioni locali (parte dichiarazioni) e un insieme di istruzioni (parte istruzioni) • I dati riferiti nel corpo possono essere costanti, variabili, oppure parametri formali • All'interno del corpo, i parametri formali vengono trattati come variabili
  • 18. Funzioni: nascita e morte • All’atto della chiamata: l’esecuzione del cliente viene sospesa e il controllo passa al servitore • Il servitore “vive” solo per il tempo necessario a svolgere il servizio • Al termine, il servitore “muore”, e l’esecuzione torna al cliente
  • 19. Chiamata di funzione • La chiamata di funzione è un’espressione della forma <nomefunzione> ( <parametri-attuali> ) • dove: <parametri-attuali> ::= [ <espressione> ] { , <espressione> }
  • 20. Funzioni: esempio Parametri formali int max (int x, int y ){ SERVITORE if (x>y) return x ; else return y; Definizione } della funzione main() { CLIENTE int z = 8; int m; Chiamata m = max ( z, 4); } della funzione Parametri attuali
  • 21. Risultato di una funzione • L’istruzione return provoca la restituzione del controllo al cliente, unitamente al valore della espressione che la segue. • Eventuali istruzioni successive alla return non saranno mai eseguite int max (int x, int y ){ if (x>y) return x ; else return y; }
  • 22. Funzioni: esempio Parametri formali int max (int x, int y ){ SERVITORE if (x>y) return x ; else return y; Definizione } della funzione main() { CLIENTE int z = 8; int m; Chiamata m = max ( z, 4); } della funzione Risultato
  • 23. Riassumendo All’atto dell’invocazione di una funzione: • si crea una nuova attivazione (istanza) del servitore • si alloca la memoria per i parametri (e le eventuali variabili locali) • si trasferiscono i parametri al servitore • si trasferisce il controllo al servitore • si esegue il codice della funzione
  • 24. Passaggio dei parametri In generale, un parametro può essere trasferito dal cliente al servitore: • per valore o copia (by value) si trasferisce il valore del parametro attuale • per riferimento (by reference) si trasferisce un riferimento al parametro attuale
  • 25. Passaggio per valore • Si trasferisce una copia del valore del parametro attuale cliente
  • 26. Passaggio per valore • Si trasferisce una copia del valore del parametro attuale Valore (copiato) di z copia Ogni azione fatta su w è locale al servitore Istanza del servitore cliente servitore
  • 27. Passaggio per riferimento • Si trasferisce un riferimento al parametro attuale cliente
  • 28. Passaggio per valore • Si trasferisce un riferimento al parametro attuale Riferimento a z (indirizzo) riferimento x x Ogni azione fatta su w è In realtà fatta sulla Istanza del variabile z servitore del cliente cliente servitore
  • 29. Passaggio dei parametri in C • In C, i parametri sono trasferiti sempre e solo per valore si trasferisce una copia del parametro attuale, non l’originale tale copia è strettamente privata e locale a quel servitore il servitore potrebbe quindi alterare il valore ricevuto, senza che ciò abbia alcun impatto sul cliente
  • 30. Passaggio dei parametri in C • In C, i parametri sono trasferiti sempre e solo per valore Conseguenza: • è impossibile usare un parametro per trasferire informazioni verso il cliente • per trasferire un’informazione al cliente si sfrutta il valore di ritorno della funzione
  • 31. Esempio Perché il passaggio per valore non basta? Problema: scrivere una procedura che scambi i valori di due variabili intere Specifica: Dette A e B le due variabili, ci si può appoggiare a una variabile ausiliaria T, e svolgere lo scambio in tre fasi Frammento di codice: int a,b,t; ... t = a; a = b; b = t; …
  • 32. Esempio • Supponendo di utilizzare, senza preoccuparsi, il passaggio per valore usato finora, la codifica potrebbe essere espressa come segue: void scambia(int a, int b) { int t; t = a; a = b; b = t; return; }
  • 33. Esempio Il cliente invocherebbe quindi la procedura così: main(){ int y = 5, x = 33; scambia(x, y); /* ora dovrebbe essere x=5, y=33 ... MA NON È VERO */ } Perché non funziona?
  • 34. Esempio: cosa è successo? • La procedura ha effettivamente scambiato i valori di A e B al suo interno • ma questa modifica non si è propagata al cliente, perché sono state scambiate le copie locali alla procedura, non gli originali • al termine della procedura, le sue variabili locali sono state distrutte, quindi nulla è rimasto del lavoro svolto dalla procedura
  • 35. Esempio: cosa è successo? • Ogni azione fatta su a e b è strettamente locale al servitore. Quindi a e b vengono scambiati, ma quando il servitore termina, tutto scompare Valori (copiati) di a e b copia x y cliente servitore
  • 36. Passaggio dei parametri in C Il C adotta sempre il passaggio per valore! È sicuro: le variabili del cliente e del servitore sono disaccoppiate ... ma non consente di scrivere componenti software il cui scopo sia diverso dal calcolo di una espressione Per superare questo limite occorre il passaggio per riferimento
  • 37. Passaggio per riferimento Il passaggio per riferimento (by reference): • non trasferisce una copia del valore del parametro attuale • ma un riferimento al parametro, in modo da dare al servitore accesso diretto al parametro in possesso del cliente • il servitore, quindi, accede direttamente al dato del cliente e può modificarlo
  • 38. Passaggio per riferimento Si trasferisce un riferimento ai parametri attuali (cioè i loro indirizzi) x y cliente
  • 39. Passaggio per riferimento Ogni azione fatta su a e b, in realtà è fatta su x e y nell’environment del cliente Riferimenti a x e y (indirizzi) α riferimento x β riferimento y cliente servitore
  • 40. Passaggio per riferimento quindi, scambiando α e β, in realtà si scambiano x e y Riferimenti a x e y (indirizzi) α riferimento x β riferimento y cliente servitore
  • 41. Realizzare il passaggio per riferimento in C • Il C non fornisce direttamente un modo per attivare il passaggio per riferimento -> a volte occorre costruirselo • è una grave mancanza! • il C lo fornisce indirettamente solo per alcuni tipi di dati • quindi, occorre costruirselo quando serve. (vedremo più avanti dei casi)
  • 42. Funzioni e strutture • Con le funzioni si possono usare come parametri e come valore di ritorno le strutture (con gli array è un po’ diverso, come vedremo) • L’utilizzo delle funzioni permette di costruire semplicemente applicazioni con la metodologia top-down • Si scriva un programma che permette di – leggere due frazioni – calcolarne la somma – visualizzare il risultato
  • 43. Esempio • Per prima cosa, definiamo le strutture dati: – una frazione è costituita da un numeratore ed un denominatore typedef struct { int num; int den; } frazione; • Poi scriviamo l’algoritmo partendo dalla versione più astratta. Scriviamo il main invocando le varie funzioni che ci servono main() { frazione f1, f2, somma; f1 = leggiFrazione(); f2 = leggiFrazione(); somma = sum(f1,f2); printf("%d/%d",somma.num,somma.den); }
  • 44. Esempio • Poi implementiamo le funzioni che abbiamo invocato nel main: frazione leggiFrazione() { frazione f; scanf("%d",&f.num); scanf("%d",&f.den); return f; }
  • 45. Esempio • La somma di due frazioni si calcola così: – calcolo il minimo comun denominatore (minimo comune multiplo dei denominatori); questo è il denominatore della somma – porto la prima frazione al comun denominatore – porto la seconda frazione al comun denominatore – calcolo la somma dei numeratori: questo è il numeratore della somma frazione sum(frazione f1, frazione f2) { int mcd; // minimo comun denominatore frazione somma; mcd = mcm(f1.den,f2.den); somma.den = mcd; f1 = portaDen(f1,mcd); f2 = portaDen(f2,mcd); somma.num = f1.num + f2.num; return somma; }
  • 46. Esempio • Infine implementiamo le funzioni che abbiamo usato nelle funzioni • Per portare una frazione ad un denominatore, devo moltiplicare numeratore e denominatore per la stessa quantità nuovoNum/nuovoDen = vecchioNum/vecchioDen • quindi nuovoNum=vecchioNum*nuovoDen/vecchioDen frazione portaDen(frazione f, int nDen) { frazione nuovo; nuovo.den = nDen; nuovo.num = f.num*nDen/f.den; return nuovo; }
  • 47. Esempio • Per calcolare il minimo comune multiplo di due interi, posso farne il prodotto e dividere per il massimo comun divisore dei due int mcm(int a, int b) { return a*b/MCD(a,b); }
  • 48. Esempio • Per calcolare il MCD di due numeri, posso usare il metodo di Euclide int MCD(int m, int n) { while (m != n) if (m>n) m=m-n; else n=n-m; return m; }
  • 49. Il programma risultante frazione sum(frazione f1, frazione f2) #include <stdio.h> { int mcd; typedef struct { int num; int den; } frazione somma; frazione; mcd = mcm(f1.den,f2.den); int MCD(int m, int n) somma.den = mcd; { while (m != n) f1 = portaDen(f1,mcd); if (m>n) f2 = portaDen(f2,mcd); somma.num = f1.num + f2.num; m=m-n; return somma; else n=n-m; } return m; frazione leggiFrazione() } { frazione f; int mcm(int a, int b) scanf("%d",&f.num); { return a*b/MCD(a,b); scanf("%d",&f.den); } return f; frazione portaDen(frazione f, int } nDen) main() { frazione nuovo; { frazione f1, f2, somma; nuovo.den = nDen; f1 = leggiFrazione(); nuovo.num = f.num*nDen/f.den; f2 = leggiFrazione(); return nuovo; somma = sum(f1,f2); } printf("%d/%d",somma.num,somma.den) ; }
  • 50. Il programma risultante • E` abbastanza facile da scrivere e da capire • E` facile da modificare • Es: voglio assicurarmi che l’utente non inserisca una frazione che ha per denominatore zero • Intervengo in una sola funzione: la leggiFrazione – è una funzione di 4 istruzioni, quindi facile da capire e da modificare frazione leggiFrazione() { frazione f; do { scanf("%d",&f.num); scanf("%d",&f.den); if (f.den==0) printf(“Re-inserire la frazionen"); } while (f.den == 0); return f; }
  • 51. Modificabilità • Ora voglio che mi fornisca solo frazioni ai minimi termini • Aggiungo una funzione riduci. Posso invocarla nel main main() { frazione f1, f2, somma; f1 = leggiFrazione(); f2 = leggiFrazione(); somma = riduci(sum(f1,f2)); printf("%d/%d",somma.num,somma.den); }
  • 52. Modificabilità • Poi definisco la nuova funzione riduci • Per ridurre una funzione ai minimi termini, basta dividere numeratore e denominatore per il loro MCD frazione riduci(frazione f) { int m = MCD(f.num,f.den); f.num = f.num/m; f.den = f.den/m; return f; }