Funzioni: passaggio di parametri per indirizzo; puntatori in C


Funzioni: passaggio di parametri per indirizzo; puntatori

Scambio

Testo

Scrivere una funzione in grado di scambiare il contenuto delle due variabili intere passate come parametri.

Implementazione

#include <stdio.h>

void swapInt (int *p1, int *p2)
{
int tmp;

tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}

void main ()
{
int n1, n2;

printf ("Inserisci i due numeri interi: ");
scanf ("%d %d", &n1, &n2);
swapInt (&n1, &n2);
printf ("n1=%d, n2=%d", n1, n2);
}

Leggi intero

Testo

Scrivere una funzione in grado di leggere un numero intero inserito dall’utente, controllando che rientri in un intervallo predefinito.

Implementazione

#include <stdio.h>
void leggiInt (int *pInt, int limInf, int limSup)
{
int dato;

do
{
scanf ("%d", &dato);
} while (dato < limInf || dato > limSup);
*pInt = dato;
}

void main ()
{
const int LIMINF = -20, LIMSUP = 30;
int n;
printf ("Inserisci numero da %d a %d: ", LIMINF, LIMSUP);
leggiInt (&n, LIMINF, LIMSUP);
printf ("n=%d", n);
}

Nota: in questo caso, poiché la funzione ha bisogno di restituire un solo dato (il numero inserito dall’utente), avrei potuto sfruttare il meccanismo del valore di ritorno (il return). Ecco la versione che passa tutti i parametri per copia e usa il meccanismo del valore di ritorno.
#include <stdio.h>
int leggiInt (int limInf, int limSup)
{
int dato;

do
{
scanf ("%d", &dato);
} while (dato < limInf || dato > limSup);
return dato;
}

int main ()
{
const int LIMINF = -20, LIMSUP = 30;
int n;
printf ("Inserisci numero da %d a %d: ", LIMINF, LIMSUP);
n = leggiInt (LIMINF, LIMSUP);
printf ("n=%d", n);
}

Indovina numero

Testo

Scrivere un programma che, utilizzando le funzioni, implementi il gioco nel quale l’utente deve indovinare un numero segreto entro un numero massimo di tentativi.
La funzione indovinaNumero():
  • Permette di inserire un numero
  • Confronta il numero da individuare con quello inserito e visualizza il messaggo “troppo grande” o “troppo piccolo”
  • Se il numero inserito è corretto, la funzione ritorna vero, altrimenti ricomincia il ciclo per un numero di volte fissato da un parametro
  • Se, esauriti i tentativi permessi, il numero segreto non è stato individuato, la funzione ritorna falso.

Implementazione

#include <stdio.h>

typedef enum {falso, vero} Booleano;

Booleano indovinaNumero (unsigned int *ripetiz,
unsigned int maxRipetiz,
unsigned long int numero)
{
unsigned long int tentativo;
unsigned int n = 0;
Booleano trovato = falso;
do
{
printf ("Tentativo %u: ", n + 1);
scanf ("%lu", &tentativo);
n++;
if (tentativo == numero)
{
trovato = vero;
}
else if (tentativo > numero)
{
printf ("Troppo grande!\n");
}
else
{
printf ("Troppo piccolo!\n");
}
}while (!trovato && n < maxRipetiz);
*ripetiz = n;
return trovato;
}

void main()
{
const unsigned long int NUMERO = 999, MAXRIP = 3;
Booleano indovinato;
unsigned int nRip;
indovinato = indovinaNumero (&nRip,MAXRIP,NUMERO);
if (!indovinato)
{
printf ("Tentativi esauriti");
}
else
{
printf ("Ok al tentativo %u", nRip);
}
}

Area

Testo

Scrivere un programma che, sfruttando le funzioni, permetta di calcolare l’area di un cerchio o di un quadrato.
L’utente inserisce un numero, dichiarando se si tratta del raggio di un cerchio o del lato di un quadrato. Se l’utente inserisce un numero negativo viene visualizzato un errore, altrimenti il sistema calcola l’area in modo appropriato.

Implementazione

#include <stdio.h>
#define PI 3.1415
#define NUMERO_NEGATIVO -1;
#define NO_ERRORE 0;

typedef enum {quadrato, cerchio} Forma;
typedef struct
{
Forma qualeForma;
float a;
float area;
} InfoFigura;

int calcolaArea (InfoFigura *f)
{
if ((*f).a < 0)
{
return NUMERO_NEGATIVO;
}
if ((*f).qualeForma == quadrato)
{
f->area = f->a * f->a;
}
else
{
f->area = f->a * f->a * PI;
}
return NO_ERRORE;
}

void main()
{
InfoFigura figura;
int errore;

printf ("Figura (0=quadrato, 1=cerchio)? ");
scanf ("%u", &figura.qualeForma);
printf ("lato o raggio: ");
scanf ("%f", &figura.a);
errore = calcolaArea (&figura);
if (errore == NO_ERRORE)
{
printf ("Area: %f\n", figura.area);
}
else
{
printf ("Errore!\n");
}
}

Notare le due sintassi equivalenti per accedere ai campi di una struct attraverso un puntatore: (*f).a
f->a
Nella soluzione dell’esercizio sono state utilizzate entrambe le sintassi ma in genere è bene sceglierne una e usare sempre quella.

 

Funzioni: parametri array


Media e varianza

Testo

  • Scrivere un programma che, utilizzando le funzioni, calcoli la media e la varianza di un array di numeri reali
  • Utilizzare le funzioni:
    • leggi(): permette di inserire il vettore di numeri
    • media(): calcola e ritorna la media
    • varianza(): calcola e ritorna la varianza
    • scrivi(): visualizza il vettore, la media e la varianza

Implementazione

#include <stdio.h>
#include <math.h>

void leggi (float vet[], unsigned int lungh);
float media (float vet[], unsigned int lungh);
float varianza (float vet[], unsigned int lungh, float media);
void scrivi (float vet[], unsigned int lungh, float media,
float var);

void main()
{
const unsigned int MAX = 3;
float array[MAX], copia[MAX];
float m, v;
int i;

leggi (array, MAX);
m = media (array, MAX);
for (i = 0; i < MAX; i++) /* Per rimediare al side effect */
{ /* che varianza() avrà su array */
copia[i] = array[i];
}
v = varianza (array, MAX, m);
scrivi (copia, MAX, m, v);
}

void leggi (float vet[], unsigned int lungh)
{
unsigned int i;

for (i = 0; i < lungh; i++)
{
printf ("Numero: ");
scanf ("%f", &vet[i]);
}
}

float media (float vet[], unsigned int lungh)
{
unsigned int i;
float m = 0;

for (i = 0; i < lungh; i++)
{
m = m + vet[i];
}
return m / lungh;
}

/* Dopo la chiamata a questa funzione, cosa conterrà il vettore passato come parametro attuale? */
float varianza (float vet[], unsigned int lungh, float media)
{
unsigned int i;
float v = 0;

for (i = 0; i < lungh; i++)
{
vet[i] = pow (vet[i] - media, 2.0); /* Modifico vet[]*/
}
for (i = 0; i < lungh; i++)
{
v = v + vet[i];
}
return v / (lungh – 1);
}

void scrivi (float vet[], unsigned int lungh, float med,
float var)
{
unsigned int i;

for (i = 0; i < lungh; i++)
{
printf ("%f\n", vet[i]);
}
printf ("Media: %f, varianza: %f", med, var);
}

In questo caso, conoscendo il side effect che varianza() ha su array, abbiamo preso delle “contromisure” nel main().
In generale però è bene cercare di ridurre al minimo i side effect. Quindi, una soluzione migliore si può trovare se è possibile modificare l’algoritmo della funzione che provoca il side effect. Nel caso particolare di questo esercizio, è possibile modificare varianza() in modo che non provochi effetti indesiderati su array. Vediamo le nuove versioni di varianza() e del main():



/* Versione che non provoca side effect */
float varianza (float vet[], unsigned int lungh, float media)
{
unsigned int i;
float v = 0;

for (i = 0; i < lungh; i++)
{
v = v + pow (vet[i] - media, 2.0);/*Non modifico vet[]*/
}
return v / (lungh – 1);
}

void main()
{
const unsigned int MAX = 3;
float array[MAX];
float m, v;
int i;

leggi (array, MAX);
m = media (array, MAX);
v = varianza (array, MAX, m); /* Adesso non ho side effect */
scrivi (array, MAX, m, v);
}

Si tenga presente che in generale non è possibile eliminare questo tipo di side effect perché gli array sono sempre passati per indirizzo e quindi qualsiasi modifica effettuata all’interno della funzione si riflette all’esterno.

fonte: http://corsi.dei.polimi.it/infoA/ 

Link per esercizi sulle funzioni: http://lia.deis.unibo.it/Courses/FondT-0809-ELT/materiale/esercizi.funzioni.pdf

Commenti

Post popolari in questo blog

Simulazioni di reti (con Cisco Packet Tracer)

Esercizi sulla rappresentazione della virgola mobile IEEE 754 (Floating Point)