Funzioni
Dichiarazione, parametri, valori di ritorno, scope, ricorsione e funzioni come puntatori.
Cos'è una Funzione?
Una funzione è un blocco di codice riutilizzabile che esegue un compito specifico. Permettono di organizzare il codice secondo il principio DRY (Don't Repeat Yourself).
#include <stdio.h>
// PROTOTIPO: dichiara che la funzione esiste (prima del main)
// tipo_ritorno nome(tipo param1, tipo param2);
double media(double a, double b);
int massimo(int x, int y);
void saluta(char nome[]); // void = non ritorna nulla
int main() {
printf("Media: %.2f\n", media(10.5, 7.3));
printf("Max: %d\n", massimo(42, 17));
saluta("Giulia");
return 0;
}
// DEFINIZIONE: il corpo della funzione
double media(double a, double b) {
return (a + b) / 2.0;
}
int massimo(int x, int y) {
return (x > y) ? x : y;
}
void saluta(char nome[]) {
printf("Ciao, %s!\n", nome);
// Nessun return (o return; senza valore)
}
Passaggio per Valore vs per Riferimento
In C, i parametri sono passati per valore di default: la funzione riceve una copia. Per modificare la variabile originale, si usa il puntatore.
#include <stdio.h>
// Passaggio per VALORE: la modifica non si propaga
void tenta_incremento(int n) {
n += 10; // Modifica la copia locale, non l'originale
printf("Dentro la funzione: n = %d\n", n);
}
// Passaggio per PUNTATORE: la modifica si propaga
void incrementa(int *n) {
*n += 10; // Dereferenzia il puntatore e modifica l'originale
}
// Scambia due valori tramite puntatori
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5;
tenta_incremento(x);
printf("Dopo tenta_incremento: x = %d\n\n", x); // Ancora 5!
incrementa(&x); // Passa l'indirizzo di x
printf("Dopo incrementa: x = %d\n\n", x); // 15
int a = 10, b = 20;
printf("Prima swap: a=%d, b=%d\n", a, b);
swap(&a, &b);
printf("Dopo swap: a=%d, b=%d\n", a, b);
return 0;
}
Dentro la funzione: n = 15 Dopo tenta_incremento: x = 5 Dopo incrementa: x = 15 Prima swap: a=10, b=20 Dopo swap: a=20, b=10
Scope e Variabili Statiche
#include <stdio.h>
int globale = 100; // Visibile in tutto il file
void dimostra_static() {
static int contatore = 0; // Inizializzata UNA SOLA VOLTA, persiste
contatore++;
printf("Chiamata n. %d\n", contatore);
}
void dimostra_scope() {
int locale = 10; // Visibile solo in questa funzione
{
int interno = 20; // Visibile solo in questo blocco
printf("interno = %d\n", interno);
}
// printf("%d", interno); // ERRORE! interno non esiste qui
printf("locale = %d, globale = %d\n", locale, globale);
}
int main() {
dimostra_scope();
dimostra_static(); // Chiamata 1
dimostra_static(); // Chiamata 2
dimostra_static(); // Chiamata 3
return 0;
}
Ricorsione
Una funzione è ricorsiva quando chiama sé stessa. Ogni funzione ricorsiva deve avere un caso base che ferma la ricorsione.
#include <stdio.h>
// Fattoriale: n! = n * (n-1)! Base: 0! = 1
long long fattoriale(int n) {
if (n <= 0) return 1; // Caso base
return n * fattoriale(n - 1); // Chiamata ricorsiva
}
// Fibonacci: f(n) = f(n-1) + f(n-2) Base: f(0)=0, f(1)=1
long long fibonacci(int n) {
if (n <= 0) return 0;
if (n == 1) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
// MCD (Massimo Comun Divisore) — algoritmo di Euclide
int mcd(int a, int b) {
if (b == 0) return a;
return mcd(b, a % b);
}
// Potenza: base^esp
double potenza(double base, int esp) {
if (esp == 0) return 1.0;
if (esp < 0) return 1.0 / potenza(base, -esp);
return base * potenza(base, esp - 1);
}
int main() {
for (int i = 0; i <= 10; i++)
printf("%d! = %lld\n", i, fattoriale(i));
printf("\nFibonacci (0-10): ");
for (int i = 0; i <= 10; i++)
printf("%lld ", fibonacci(i));
printf("\n");
printf("\nMCD(48, 18) = %d\n", mcd(48, 18));
printf("2^10 = %.0f\n", potenza(2.0, 10));
return 0;
}
Puntatori a Funzione
In C le funzioni hanno un indirizzo in memoria. Un puntatore a funzione ti permette di passare funzioni come argomenti ad altre funzioni.
#include <stdio.h>
int somma(int a, int b) { return a + b; }
int diff(int a, int b) { return a - b; }
int prodotto(int a, int b){ return a * b; }
// Funzione che accetta un puntatore a funzione come parametro
int applica(int a, int b, int (*operazione)(int, int)) {
return operazione(a, b);
}
int main() {
// Sintassi: tipo (*nome_puntatore)(tipi_parametri)
int (*op)(int, int);
op = somma;
printf("Somma: %d\n", op(10, 5));
op = diff;
printf("Diff: %d\n", op(10, 5));
op = prodotto;
printf("Prodotto: %d\n", op(10, 5));
// Array di puntatori a funzione
int (*operazioni[3])(int, int) = {somma, diff, prodotto};
char *nomi[] = {"somma", "diff", "prodotto"};
for (int i = 0; i < 3; i++) {
printf("%s(7, 3) = %d\n", nomi[i], operazioni[i](7, 3));
}
// Passaggio come argomento
printf("\napplica(10,5,somma) = %d\n", applica(10, 5, somma));
printf("applica(10,5,prodotto) = %d\n", applica(10, 5, prodotto));
return 0;
}
Scrivi le funzioni: perimetro_cerchio(r), area_cerchio(r), volume_sfera(r). Poi scrivi un main che le chiami con r=5.0 e stampi i risultati con 4 decimali. Usa la costante M_PI da math.h (o definiscila tu).
#include <stdio.h>
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
double perimetro_cerchio(double r) { return 2.0 * M_PI * r; }
double area_cerchio(double r) { return M_PI * r * r; }
double volume_sfera(double r) { return (4.0 / 3.0) * M_PI * r * r * r; }
int main() {
double r = 5.0;
printf("Raggio: %.1f\n", r);
printf("Perimetro cerchio: %.4f\n", perimetro_cerchio(r));
printf("Area cerchio: %.4f\n", area_cerchio(r));
printf("Volume sfera: %.4f\n", volume_sfera(r));
return 0;
}Scrivi una funzione ricorsiva palindromo(str, inizio, fine) che controlli se una stringa è palindroma (uguale letta al contrario). Es: "racecar", "level", "madam". Testa la funzione con almeno 5 stringhe.
#include <stdio.h>
#include <string.h>
int palindromo(char str[], int inizio, int fine) {
if (inizio >= fine) return 1; // Caso base
if (str[inizio] != str[fine]) return 0; // Non palindromo
return palindromo(str, inizio + 1, fine - 1);
}
void testa(char *s) {
int len = strlen(s);
printf("%-12s → %s\n", s,
palindromo(s, 0, len-1) ? "PALINDROMO" : "Non palindromo");
}
int main() {
testa("racecar");
testa("level");
testa("madam");
testa("hello");
testa("abcba");
testa("A");
testa("ab");
return 0;
}Implementa la soluzione ricorsiva alla Torre di Hanoi. La funzione hanoi(n, da, a, via) sposta n dischi dal piolo da al piolo a usando via come appoggio. Stampa ogni mossa e il numero totale di mosse.
#include <stdio.h>
int mosse = 0;
void hanoi(int n, char da, char a, char via) {
if (n == 1) {
printf("Sposta disco 1: %c → %c\n", da, a);
mosse++;
return;
}
hanoi(n-1, da, via, a); // Sposta n-1 dischi su via
printf("Sposta disco %d: %c → %c\n", n, da, a);
mosse++;
hanoi(n-1, via, a, da); // Sposta n-1 dischi su a
}
int main() {
int n;
printf("Numero di dischi: ");
scanf("%d", &n);
hanoi(n, 'A', 'C', 'B');
printf("\nTotale mosse: %d (teorico: %d)\n", mosse, (1 << n) - 1);
return 0;
}Implementa il Bubble Sort come funzione bubble_sort(arr, n, confronto) dove confronto è un puntatore a funzione. Crea due funzioni di confronto: crescente e decrescente. Poi usa la stessa funzione per ordinare un array in entrambi i modi.
#include <stdio.h>
int crescente(int a, int b) { return a > b; }
int decrescente(int a, int b){ return a < b; }
void bubble_sort(int arr[], int n, int (*cmp)(int, int)) {
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (cmp(arr[j], arr[j+1])) {
int tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp;
}
}
}
}
void stampa(int arr[], int n) {
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
}
int main() {
int a[] = {64, 25, 12, 22, 11, 90, 3};
int n = 7;
printf("Originale: "); stampa(a, n);
bubble_sort(a, n, crescente);
printf("Crescente: "); stampa(a, n);
bubble_sort(a, n, decrescente);
printf("Decrescente:"); stampa(a, n);
return 0;
}Implementa Fibonacci con memoization: usa un array statico per memorizzare i risultati già calcolati. Confronta il numero di chiamate con e senza memoization per n=30. La versione naive fa ~2³⁰ chiamate, quella con memo fa ~2n.
#include <stdio.h>
#include <string.h>
#define MAX 100
long long memo[MAX];
int chiamate_memo = 0;
int chiamate_naive = 0;
long long fib_memo(int n) {
chiamate_memo++;
if (n <= 1) return n;
if (memo[n] != -1) return memo[n]; // Già calcolato!
memo[n] = fib_memo(n-1) + fib_memo(n-2);
return memo[n];
}
long long fib_naive(int n) {
chiamate_naive++;
if (n <= 1) return n;
return fib_naive(n-1) + fib_naive(n-2);
}
int main() {
int n = 30;
// Memoization
memset(memo, -1, sizeof(memo));
long long risultato = fib_memo(n);
printf("Fibonacci(%d) = %lld\n", n, risultato);
printf("Chiamate con memo: %d\n", chiamate_memo);
// Naive
fib_naive(n);
printf("Chiamate naive: %d\n", chiamate_naive);
printf("Speedup: %.0fx\n", (double)chiamate_naive / chiamate_memo);
return 0;
}