Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
L’esperienza ha dimostrato che il modo migliore, per sviluppare e amministrare un programma corposo, è di
costruirlo partendo da pezzi più piccoli o moduli ognuno dei quali sia più maneggevole del programma
originale. Questa tecnica è detta divide et impera.
I moduli in C sono chiamati funzioni. I programmi C sono scritti tipicamente combinando le nuove funzioni
scritte dal programmatore con quelle "preconfezionate" disponibili nella libreria standard del C. In questo
capitolo discuteremo di entrambi i tipi di funzione.
La libreria standard del C fornisce una ricca collezione di funzioni per eseguire i comuni calcoli matematici,
per la manipolazione delle stringhe e dei caratteri, per l’input/output e per molte altre operazioni utili. Ciò
renderà più semplice il lavoro del programmatore, poiché le suddette funzioni forniranno molte delle
capacità di cui egli avrà bisogno.
Le funzioni sono invocate da una chiamata di funzione che specifica il nome della funzione e fornisce delle
informazioni (gli argomenti) di cui la funzione chiamata ha bisogno per completare le attività per le quali è
stata progettata.
È buona abitudine familiarizzare con la ricca collezione di funzioni incluse nella libreria standard del C.
Di seguito una lista delle funzioni comunemente utilizzate della libreria matematica:
ESERCIZIO:
Scrivere un programma che visualizzi la radice quadrata di tutti i numeri interi tra 1 e 10
#include <stdio.h>
#include <math.h>
int main () {
int a;
for (a=1;a<=10;a++){
printf("La radice quadrata di %d e': %.3f\n", a, sqrt(a));
Tutte le variabili dichiarate nelle definizioni di funzione sono definite locali poiché visibili e modificabili
soltanto dalla funzione che le ha definite. Le variabili definite esternamente invece, sono definite globali e
possono essere utilizzate e modificate da tutte le funzioni definite.
Ognuno dei programmi che abbiamo presentato fino ad ora è stato formato da una funzione chiamata
main che, per eseguire i propri compiti, ha richiamato quelle della libreria standard.
Consideriamo ora in che modo i programmatori possano scrivere e richiamare le proprie funzioni
personalizzate al di fuori della funzione main.
È buona norma (e solitamente viene fatto) dichiarare un prototipo tutte le funzioni all'inizio del programma,
in modo da ovviare a eventuali problemi di ordine di elaborazione.
Un prototipo è rappresentato in questo modo: tipo_di_output nome_della_funzione (lista_di_input);
Il tipo di valore di ritorno void indica che una funzione non restituirà alcun valore.
La lista dei parametri è un elenco che specifica i parametri, separati da virgole, che saranno ricevuti dalla
funzione quando sarà chiamata. Qualora una funzione non riceva alcun valore, la lista dei parametri sarà
void. Salvo che non sia un tipo di dato int, ognuno dei parametri dovrà essere specificato esplicitamente
(tuttavia è consigliabile includere il tipo di dato per ogni parametro anche qualora questo fosse di tipo int).
Il compilatore farà riferimento al prototipo della funzione, per controllare che le chiamate della funzione
contengano il tipo di ritorno corretto, il numero e tipi appropriati per gli argomenti e che questi siano forniti
nell'ordine corretto.
Le dichiarazioni e le istruzioni inserite all’interno delle parentesi graffe formano il corpo della funzione
(anche definito blocco). Le variabili potranno essere dichiarate in qualsiasi blocco e questi potranno essere
nidificati. Una funzione non può essere mai definita all’interno di un’altra funzione.
ESERCIZIO: Crea un programma che trovi il max tra 3 numeri
#include<stdio.h>
int maximum (int x, int y, int z); //inseriamo il nostro prototipo di funzione
int main () {
int num1;
int num2;
int num3;
return 0;
}
if (y > max){
max = y;
}
if (z > max){
max = z;
}
return max;
}
Una chiamata di funzione che non corrisponde al suo prototipo è un errore di sintassi. Sarà generato un
errore anche qualora il prototipo e la definizione della funzione non concordino.
main() {
float y;
printf("Immettere il numero del quale si vuole conoscere la radice quadrata: \t");
scanf("%f",&y);
printf("%f\n", radiceq(y));
Se in questo caso non avessimo immesso il prototipo di funzione prima del main, avremmo avuto un
warning!
Il compilatore, seguendo l’ordine, avrebbe iniziato a fare supposizioni sulla funzione radiceq(y) e non
sarebbe stato in grado di capire di cosa si trattasse. Immettendo dei prototipi all’inizio di ogni compilazione
possiamo ovviare al problema dell’ordine e richiamare funzioni dove vogliamo senza problemi.
ESERCIZIO:
Risolvi il problema delle torri di Hanoi per n dischi e n+1 pioli (n = 9).
#include <stdio.h>
void hanoi(int);
int n=9;
int c=0;
main (){
printf("La soluzione al problema delle torri di Hanoi con 9 pioli e 10 dischi risulta essere:\n\n");
hanoi(9);
}
void hanoi(int n){
while (c<n){
printf("Sposta il disco da 0 a %d\n",c+1);
c=c+1;
}
c=n-1;
while (c>=1){
printf("Sposta il disco da %d a 10\n",c);
c=c-1;
}
}
ESERCIZIO:
Disegna un rettangolo di dimensione n×m (10×8) .
#include <stdio.h>
void disegna_rettangolo(int,int);
void main(){
disegna_rettangolo(10,8);
}
/* lato superiore */
for(i = 0; i < w; i++){
printf("*");
}
printf("\n");
/* lati sx e dx */
for(j = 0; j < h-2; j++){
printf("*");
for(i = 0; i < w-2; i++){
printf(" "); /*regola il vuoto interno*/
}
printf("*");
printf("\n");
}
/* lato inferiore */
for(i = 0; i < w; i++){
printf("*");
}
printf("\n");
}
Lo stack delle chiamate di funzione e i record di attivazione
Per comprendere con il C effettui le chiamate di sistema, è necessario pensare ad una struttura dati nota come
stack. Possiamo pensare ad uno stack come a qualcosa di analogo ad una pila di piatti. Quando un piatto
viene sistemato nella pila, esso viene normalmente posizionato in cima (push). Similmente, quando un piatto
viene rimosso dalla pila, la rimozione avviene sempre dalla cima (pop). Gli stack sono conosciuti come
strutture dati last-in, first-out (LIFO), ovvero, l’ultimo elemento inserito è anche il primo ad essere rimosso.