Skillbook Logo
foto profilo

Skill Factory

Lista post > Laboratori di Logica di Programmazione in C- Lezione 4

Laboratori di Logica di Programmazione in C- Lezione 4

Gino Visciano | Skill Factory - 25/03/2018 19:49:40 | in Tutorials

Benvenuti, nelle lezioni precedenti, per svolgere i nostri laboratori, abbiamo usato diverse funzioni, includendole  nei programmi attraverso librerie predefinite. in questa lezione introduciamo il concetto di funzioni definite dall'utente, con l'obiettivo di creare librerie di funzioni personalizzate.

Una funzione è un task di programmazione che qundo viene eseguito, può avere da 0 ad n dati in ingresso detti argomenti oppure parametri e restituire da 0 ad 1 risultato.
Le funzioni sono BLACK - BOX che restituiscono sempre lo stesso risultato se vengono passati gli stessi argomenti. 

Una funzione che non restiruisce  un risultato viene chiamata procedura.

L'uso delle funzioni e delle procedure rende i programmi più compatti ed affidabili, riduce i tempi di sviluppo e diventa più semplice testarli.

Il linguaggio C oltre ad essere Imperativo (composto da istruzioni e strutture logiche di programmazione), è anche Funzionale, perché permette sia di creare funzioni personalizzate, sia di usare funzioni di libreria che arricchiscono le potenzialità del linguaggio.

STRUTTURA DI UNA FUNZIONE

Se un funzione non restituisce un risultato, ovvero è una procedura, come tipo restituito dovete indicare void.

Gli argomenti di una funzione sono variabili locali, cioè sono visibili solo all'inteno della funzione,  quando la funzione termina non esistono più.
Nel corpo della funzione divisione gli argomenti dividendo e divisore vengono utilizzati per eseguire il calcolo richiesto.
Il risultato del calcolo viene assegnato alla variabile locale risultato che è anche il valore restituito con il comando return.

L'esempio seguente mostra come utilizzare la funzione divisione:

Esempio 1

#include <stdio.h>

/* Dichiarazione della funzione divisione */
double divisione(double dividendo, double divisore);
int main(){
/* %lf visualizza dove viene inserito il contenuto della variabile double corrispondente */
printf("Risultato %.2lf/%.2lf=%.2lf",15.0,3.0,divisione(15.0,3.0));
return(0);
}

/* Implementazione della funzione divisione */
double divisione(double dividendo, double divisore){
double risultato;
if (divisore>0){
    risultato=dividendo/divisore;
     } else {
    risultato=-1;
   }
return risultato;
}

PASSAGGIO DI ARGOMENTI ALLE FUNZIONI PER VALORE

Di default quando si passano gli argomenti ad un funzione, vengono passati per valore. Questo significa che vengono create nuove variabili locali all'interno dell'area di memoria che appartiene alla funzione e quando la funzione termina non esistono più, come mostra lo schema seguente:

1) In main è presente la variabile risultato=0.0;
2) Quando si esegue la funzione divisione, in main risultato=0.0, mentre in divisione vengono create tre variabili:
     risultato=0.0;
     dividendo=15.0;
     divisore=3.0;
3) Quando si esegue il calcolo in divisione risultato diventa 5.0;
4) Quando termina la funzione divisione, tutte le variabili locali di divisione vengono cancellate e per effetto del return risultato di main diventa uguale a 5.0.

PASSAGGIO DI ARGOMENTI ALLE FUNZIONI PER RIFERIMENTO

In C un riferimento è un indirizzo di memoria dov'è memorizzato un valore, come  mostra l'immagine seguente:

In realtà tutte le variabili, per gestire i contenuti corrispondenti, usano riferimenti (posizioni/indirizzi di memoria), in C per ottenere il riferimento associato ad una variabile basta far precedere il nome da una &, come mostra l'esempio seguente:

&a => A00FF011 => 10 (valore corrispondente in memoria).

Le variabili che contengono un riferimento si chiamano puntatori, si distinguono dalle variabili classiche perchè i nomi sono preceduti da un *, come mostra l'esempio seguente:

Esempio 2

int main(){
    int a=10;
    int *p=&a;
    printf("a=%d, *p=%d, p=%p", a, *p, p);
}

a=>nome varibile intera che contiene il valore 10;
*p=nome puntatore, attraverso il riferimento (indirizzo) assegnato permette di gestire il valore 10, ha lo stesso compostamento della variabile a;
p= nome varibile che contiene il riferimento (indirizzo) del valore 10;

quindi se *p=&a, allora:

a => 10:
*p => 10;
p => A00FF011.

Infatti, eseguendo l'esempio 2, si ottiene l'outpit seguente:

Per capire a cosa servono i puntatori ed i riferimenti facciamo due esemp:i

Immaginate di voler creare una funzione che inverte i valori contenuti in due variabili a e b.

Nel primo caso creiamo la funzione inverti e passiamo a e b  per valore, come nell'esempio seguente:

Esempio 3

void inverti(int a, int b);
int main(){
    int a=10;
    int b=20;
    printf("1) Main[a=%d - b=%d]\n",a,b);
    inverti(a,b);
    printf("3) Main[a=%d - b=%d]\n",a,b);
   return(0);
}

void inverti(int a,int b){
    int temp;
    temp=a;
    a=b;
    b=temp;
    printf("2) Inverti[a=%d - b=%d]\n",a,b);
}

Osservando i risultati potete capire che la funzione inverti ,con il passaggio degli argomenti per valore, ha invertito a e b locali, mentre a e b di main sono rimasti gli stessi.
Questo significa che quando eseguiamo una funzione e passiamo gli argomenti attesi per valore, viene creata una copia di quelli originali, come mostra lo schema seguente:

Nel secondo caso creiamo la funzione inverti, ma questa volta non passiamo i valori di a e b, ma i loro  riferimenti.

Passando alla funzione inverti i riferimenti di a e b, diventa posibile modificare direttamente i loro valori, naturalemente gli argomenti della funzione inverti non sono variabili ma puntatori, come mostra l'esempio seguente:

Esempio 4

void inverti(int *a,int *b);
int main(){
    int a=10;
    int b=20;
    printf("1) Main[a=%d - b=%d]\n",a,b);
    inverti(&a,&b);
    printf("3) Main[a=%d - b=%d]\n",a,b);
   return(0);
}

void inverti(int *a,int *b){
    int temp;
    temp=*a;
    *a=*b;
    *b=temp;
    printf("2) Inverti[a=%d - b=%d]\n",*a,*b);
}

Osservando i risultati, adesso vediamo che passando alla funzione inverti i riferimenti di a e b, cioè &a ed &b,  i valori di a e b di main sono stati invertiti.

Lo schema seguente descrive cosa accade in memoria durante l'esecuzione del programma:

 

FUNZIONI RICORSIVE

In C le funzioni sono ricorsive, questa proprietà è molto importante perché, grazie alla ricorsività, una funzione può richiamare se stessa.
Per capire praticamente la ricorsione vediamo un esempio.

Calcoliamo il fattoriale di un numero n, valore che si indica con il simbolo:

n!

Per cacloare Il fattoriale di un numero n dovete moltiplicare tra loro tutti i numeri compresi tra 1 ed n, come mostra la formula seguente:

1 * 2 * ... * n-1 * n

Ad esempio:

0! = 1 per convenzione;
1! = 1 * 1 = 1;
2! = 1 * 2 = 2;
3! = 1 * 2 * 3 = 6.

L'esempio seguente mostra come calcolare il fattoriale di n utilizzando una funzione ricorsiva in C.

Esempio 5

int fattoriale(int numero);
int main(){
   int numero;
   printf("Calcolo del Fattoriale\n");
   printf("\nNumero:");
   /* %d = input numerico,
   &numero passiamo alla funzione scanf il riferimento della variabile numero */

   scanf("%d",&numero);
   printf("Fattoriale di %d = %d\n", numero, fattoriale(numero));
   return(0);
}
int fattoriale(numero){
    if (numero==0)
        return 1;
    else
        return numero * fattoriale(numero-1);
}

La funzione fattoriale richiama in modo ricorsivo se stessa, passando ogni volta come argomento il valore numero-1.
Quando numero diventa uguale a zero la ricorsione s'interrompe ed i return di tutte le funzioni chiamate vengono eseguiti calcolando il risultato della formula numero * fattoriale (numero - 1), come mostra lo schema seguente:

Osservando il comportamento della funzione fattoriale si capisce che la ricorsione è una tecnica che permette di ripetre più volte le stesse operazioni, come accade per i cicli, infatti il fattoriale può essere calcolato anche nel modo seguente:

Esempio 6

int main(){
   int numero,x;
   int fattoriale=1;
   printf("Calcolo del Fattoriale\n");
   printf("\nNumero:");
   /* %d = input numerico,
   &numero passiamo alla funzione scanf il riferimento della variabile numero */

   scanf("%d",&numero);
   for(x=1;x<=numero;x++){
  /* fattoriale = fattoriale * x => fattoriale*=x */
      fattoriale*=x;
   }
   printf("Fattoriale di %d = %d\n", numero, fattoriale);
   return(0);
}

 

 

Share Button
TOP