Sviluppo top-down: scomposizione in moduli e funzioni
Scomposizione Top-Down Problema / Obiettivo Modulo A Modulo B Modulo C funzione A1(…) funzione A2(…) funzione B1(…) funzione B2(…) funzione C1(…) funzione C2(…) Ogni modulo è indipendente e riusabile; le funzioni sono foglie operative (incapsulazione).

Chiamata/ritorno di funzione e record di attivazione nello stack
Call → Stack frame → Return Stack Frame main vars locali, return addr Frame f(x,y) parametri, locali main() f(int x, int y) call f(x,y) return (val) Ogni chiamata crea un record (frame) sullo stack con parametri, locali e indirizzo di ritorno. Al return il frame viene rimosso; il valore di ritorno torna al chiamante.
I diagrammi illustrano la scomposizione top-down e il meccanismo di call/return con stack frames.

Lezione 1 – Lo sviluppo Top-Down e le funzioni

I programmi visti negli esempi precedenti riguardavano problemi relativamente semplici.
Quando il livello di complessità aumenta, conviene suddividere il programma in sottoprogrammi
secondo la metodologia di sviluppo top-down.

Si tratta in sostanza di definire quali siano le parti fondamentali di cui si compone il programma
(top), e procedere poi al dettaglio di ogni singola parte (down).
Ogni parte, che compone il programma, corrisponde ad un modulo che svolge
una specifica funzionalità per la risoluzione del problema, cioè il programma viene scomposto
in moduli funzionalmente indipendenti.

Nota:
Questa metodologia facilita il lavoro di manutenzione del software, perché si può intervenire con modifiche o correzioni su un solo modulo, avendo nel contempo cognizione di quello che fa l’intero programma.
Inoltre alcuni moduli, essendo funzionalmente indipendenti, possono essere riutilizzati, senza bisogno di grandi modifiche, all’interno di altri programmi.

Nel linguaggio C i sottoprogrammi sono rappresentati attraverso le funzioni.

Funzioni built-in

Nei programmi presentati precedentemente sono già state utilizzate le funzioni, anche se sono state identificate come istruzioni del linguaggio C.
L’istruzione scanf è in realtà una funzione che serve a ricevere i dati che l’utente introduce da tastiera;
l’istruzione printf è una funzione che si occupa di mandare sul video i caratteri corrispondenti ai dati o ai messaggi di output.
Inoltre abbiamo utilizzato funzioni di calcolo, quali la funzione sqrt per la radice quadrata di un numero.

Sono tutti esempi di funzioni predefinite nel linguaggio C o, come si dice nel linguaggio informatico,
funzioni built-in, cioè moduli software che il programmatore può usare senza implementarli.
Questi moduli vengono utilizzati indicandone semplicemente il nome.

Dichiarazione delle funzioni

La dichiarazione delle funzioni che si intendono utilizzare nel programma va posta in testa al programma,
immediatamente dopo la sezione dedicata alle direttive e alla dichiarazioni delle variabili del programma.

Sintassi generale:

tipo_di_dato_restituito nome_funzione (elenco_parametri) {
    /* istruzioni */
    return valore_restituito; /* se non void */
}

Esempi di funzioni

Funzione con ritorno di valore:

int Somma(int a, int b){
    int x = a + b;
    return x;
}

Funzione senza ritorno (void):

void StampaSomma(int a, int b){
    int x = a + b;
    printf("Il totale è: %d\n", x);
}

Funzione senza parametri:

void Stampa(void){
    printf("Stampa di prova\n");
    printf("Fine della funzione\n");
}

La chiamata di una funzione avviene specificandone il nome seguito dall’elenco dei parametri (se previsti). Esempio:

int totale;
int subtot1, subtot2;
/* ... */
totale = Somma(subtot1, subtot2);

Esercizi

1) Identifica tre vantaggi concreti dell’approccio top-down su un problema reale
Esempi: modularità, riuso, manutenzione, test unitari, parallelizzazione del lavoro.
2) Trasforma un programma monolitico in 3 moduli e descrivi le interfacce
Per ogni modulo descrivi responsabilità, ingressi/uscite (parametri/return), dipendenze.
3) Elenca 5 funzioni “built-in” che hai già usato e il relativo header
Esempio: printf/scanf<stdio.h>, sqrt/pow<math.h>, strlen/strcmp<string.h>.

Torna all’indice ↑

Lezione 2 – Il main() e l’equazione di secondo grado (funzioni senza parametri)

Il main è a tutti gli effetti una funzione: questo è il motivo della presenza della coppia di parentesi
tonde dopo la parola main. Tuttavia la funzione main ha una caratteristica specifica: è la funzione
che viene eseguita per prima all’avvio del programma. L’istruzione return può essere presente anche
nella funzione main; in tal caso il valore viene restituito direttamente al sistema operativo, che è il
programma chiamante della funzione. Per completezza, negli esempi indicheremo int prima di main e
alla fine scriveremo return 0;.

Nota: L’uso di return 0 rappresenta la terminazione “senza errori” di un programma.

Esempio completo: soluzioni di un’equazione di secondo grado (versione con variabili globali)

Un’equazione di secondo grado scritta nella forma ax² + bx + c = 0 è caratterizzata dai coefficienti
a, b, c. Dopo aver calcolato il discriminante Δ (delta) con la formula b * b - 4 * a * c, si possono
riconoscere tre situazioni:

  • Δ < 0 → non esistono soluzioni reali,
  • Δ = 0 → le due soluzioni reali sono coincidenti,
  • Δ > 0 → ci sono due soluzioni reali distinte.

Se esistono soluzioni reali, queste si ottengono dalla formula
(-b ± sqrt(Δ)) / (2 * a).
Occorre inoltre prevedere il caso a = 0: l’equazione si riduce a primo grado bx + c = 0, con i sottocasi:

  • b = 0 e c = 0 → equazione indeterminata,
  • b = 0 e c ≠ 0 → equazione impossibile,
  • b ≠ 0 → soluzione unica x = -c / b.
Programma C

/* Equazioni.c : soluzioni di un'equazione di secondo grado */
#include <stdio.h>
#include <math.h>

/* input */
double a, b, c;   /* coefficienti equazione */

/* output */
double x1, x2;

/* lavoro */
double delta;     /* discriminante */

void RisolviPrimoGrado() {
    if ((b == 0.0) && (c == 0.0)) {
        printf("Equazione indeterminata \n");
    } else {
        if (b == 0.0) printf("Equazione impossibile \n");
        else          printf("x = %8.3f \n", -c / b);
    }
    return;
} /* RisolviPrimoGrado */

double CalcolaDelta() {
    delta = b * b - 4.0 * a * c;
    return delta;
} /* CalcolaDelta */

void ScriviSoluzioni() {
    if (delta < 0.0) {
        printf("Non esistono soluzioni reali \n");
    } else if (delta == 0.0) {
        x1 = -b / (2.0 * a);
        printf("x1 = x2 = %8.3f \n", x1);
    } else {
        x1 = (-b - sqrt(delta)) / (2.0 * a);
        x2 = (-b + sqrt(delta)) / (2.0 * a);
        printf("x1 = %8.3f \n", x1);
        printf("x2 = %8.3f \n", x2);
    }
    return;
} /* ScriviSoluzioni */

/* funzione principale */
int main(void) {
    printf("Tre coefficienti: ");
    if (scanf("%lf %lf %lf", &a, &b, &c) != 3) { puts("Input non valido"); return 1; }

    if (a != 0.0) {
        CalcolaDelta();
        ScriviSoluzioni();
    } else {
        RisolviPrimoGrado();
    }
    return 0;
}

Osservazioni didattiche:

  • In questo esempio tutte le funzioni sono senza parametri.
  • CalcolaDelta restituisce un double; RisolviPrimoGrado e ScriviSoluzioni sono void.
  • Le variabili a, b, c, x1, x2, delta sono globali (dichiarate fuori da main).
  • Serve <math.h> per usare sqrt; con GCC linka con -lm.

Focus su main() e return

Nel listato, main legge i tre coefficienti, decide se trattare l’equazione di secondo grado
(a != 0) oppure richiamare la funzione per il primo grado. Il return 0; finale restituisce al sistema
operativo lo stato “nessun errore”.

Esercizi suggeriti

1) Stampare anche un messaggio specifico quando Δ = 0 (radici coincidenti)
Già presente nel ramo delta == 0.0.
2) Formattare le radici con tre cifre decimali (%8.3f)
Già applicato in stampa.
3) Validare l’input
Controlla il valore di ritorno di scanf; eventualmente ripeti la lettura in un ciclo.

Torna all’indice ↑

Lezione 3 – Funzioni con parametri (riuso, locali/globali, parametri formali e attuali)

L’uso delle funzioni risponde all’esigenza di costruire programmi ben organizzati secondo la metodologia top-down.
Un’altra esigenza è il riuso dello stesso gruppo di istruzioni in contesti diversi: per questo si rendono parametrici i valori
utilizzati dalle funzioni, passando loro gli argomenti dalla funzione chiamante (main o un’altra funzione).

Idea chiave: una funzione “parametrica” è più riutilizzabile perché non dipende da variabili globali:
riceve i dati in ingresso come parametri e restituisce (eventualmente) un risultato di ritorno.

Esempio: equazione di secondo grado – versione con parametri

Riprendiamo l’esempio dell’equazione di secondo grado ax² + bx + c = 0, introducendo parametri nelle funzioni.
Questo elimina la dipendenza da variabili globali e rende i moduli più riusabili.

Programma C – Equazioni2.c

/* Equazioni2.c : soluzioni di un'equazione di secondo grado */
#include <stdio.h>
#include <math.h>

void RisolviPrimoGrado(double c2, double c3) {
    if ((c2 == 0.0) && (c3 == 0.0)) {
        printf("Equazione indeterminata \n");
    } else {
        if (c2 == 0.0) printf("Equazione impossibile \n");
        else           printf("x = %8.3f \n", -c3 / c2);
    }
}

double CalcolaDelta(double c1, double c2, double c3) {
    return c2 * c2 - 4.0 * c1 * c3;
}

void ScriviSoluzioni(double c1, double c2, double c3, double d) {
    double x1, x2;
    if (d < 0.0) {
        printf("Non esistono soluzioni reali \n");
    } else if (d == 0.0) {
        x1 = -c2 / (2.0 * c1);
        printf("x1 = x2 = %8.3f \n", x1);
    } else {
        x1 = (-c2 - sqrt(d)) / (2.0 * c1);
        x2 = (-c2 + sqrt(d)) / (2.0 * c1);
        printf("x1 = %8.3f \n", x1);
        printf("x2 = %8.3f \n", x2);
    }
}

/* funzione principale */
int main(void) {
    double a, b, c;   /* coefficienti equazione */
    double delta;     /* discriminante */

    printf("Tre coefficienti: ");
    if (scanf("%lf %lf %lf", &a, &b, &c) != 3) return 1;

    if (a != 0.0) {
        delta = CalcolaDelta(a, b, c);
        ScriviSoluzioni(a, b, c, delta);
    } else {
        RisolviPrimoGrado(b, c);
    }
    return 0;
}

Osservazioni:

  • Le variabili a, b, c, delta sono locali a main. Le altre funzioni operano sui parametri.
  • CalcolaDelta restituisce un double, mentre RisolviPrimoGrado e ScriviSoluzioni sono void.
  • Le variabili x1 e x2 sono locali a ScriviSoluzioni.

Variabili locali e globali (richiamo)

Le variabili dichiarate dentro una funzione sono locali e visibili solo in quell’ambito.
Le variabili dichiarate fuori da tutte le funzioni sono globali e visibili ovunque.
Nei progetti reali è buona pratica minimizzare le globali, per migliorare modularità, testabilità e manutenzione.

Parametri formali e parametri attuali

Si chiama passaggio di parametri l’operazione con cui il chiamante invia i valori alla funzione.
I nomi scritti nella firma della funzione (ad esempio double c1, double c2, double c3) sono i parametri formali.
Le variabili usate nella chiamata (ad esempio a, b, c in CalcolaDelta(a, b, c)) sono i parametri attuali.

Esempio mirato:

/* formali */   double CalcolaDelta(double c1, double c2, double c3);
/* attuali */   delta = CalcolaDelta(a, b, c);

I valori di a, b, c vengono copiati nei parametri formali c1, c2, c3.

Benefici principali delle funzioni con parametri:

  1. Riuso del codice (stesse funzioni, dati diversi).
  2. Incapsulazione (i dettagli restano dentro la funzione).
  3. Manutenzione semplificata (modifica locale, effetto controllato).
  4. Testabilità (si testano i moduli isolatamente).

Mini-esercizi

1) Restituire un codice di esito invece delle stampe
−1 nessuna radice reale, 0 radici coincidenti, 1 due radici distinte.
2) Restituire le radici via parametri di output
In C usa puntatori double*; in C++ referenze double&.
3) Separare I/O ed elaborazione
Tre funzioni: leggi(), calcola(), stampa() per responsabilità singola.

Torna all’indice ↑

Lezione 4 – Passaggio di parametri: per valore vs per indirizzo

Nel linguaggio C il passaggio dei parametri può avvenire in due modi:

  • Per valore: la funzione riceve una copia dei valori; le modifiche sui parametri formali non influenzano le variabili del chiamante.
  • Per referenza (per indirizzo): la funzione riceve l’indirizzo delle variabili del chiamante; le modifiche ai parametri formali si riflettono sulle variabili originali.
Strumenti in C: per “per indirizzo” si passano &variabile e si usano puntatori nella firma della funzione.
Dentro la funzione si dereferenzia con *p per leggere/scrivere il valore puntato.

Esempio – Ordinamento crescente di tre numeri (versione base)

Programma C – TreNumeri.c

/* TreNumeri.c : ordinamento crescente di tre numeri */
#include <stdio.h>

int main(void) {
    int a, b, c, temp;

    printf("Tre numeri: ");
    scanf("%d %d %d", &a, &b, &c);

    if (a > b) { temp = a; a = b; b = temp; }
    if (a > c) { temp = a; a = c; c = temp; }
    if (b > c) { temp = b; b = c; c = temp; }

    printf("Numeri ordinati:\n%d\n%d\n%d\n", a, b, c);
    return 0;
}

Idea di modularizzazione: raggruppare la logica “controlla e scambia due variabili” in una funzione Ordina e riutilizzarla più volte.

Passaggio per indirizzo: funzione Ordina con puntatori

Programma C – TreNumeri2.c

/* TreNumeri2.c : ordinamento crescente di tre numeri */
#include <stdio.h>

void Ordina (int *x, int *y) {
    int temp;
    if (*x > *y) {
        temp = *x;
        *x = *y;
        *y = temp;
    }
    return;
}

/* funzione principale */
int main(void) {
    int a, b, c;

    printf("Tre numeri: ");
    scanf("%d %d %d", &a, &b, &c);

    Ordina(&a, &b);
    Ordina(&a, &c);
    Ordina(&b, &c);

    printf("Numeri ordinati:\n%d\n%d\n%d\n", a, b, c);
    return 0;
}

Perché funziona? Ordina riceve gli indirizzi di a, b, c. Usando *x e *y (dereferenziazione) modifica i valori nelle celle di memoria originali.

Per valore vs per indirizzo: esempi minimi

Programma C – Parametri1.c (per valore)

/* Parametri1.c : per valore */
#include <stdio.h>

void Modifica (int y) {
    y += 2;
}

int main(void) {
    int x = 1;
    printf("%d \n", x);
    Modifica(x);
    printf("%d \n", x);
    return 0;
}

Output: 1 e 1. La funzione ha modificato la copia, non x.

Programma C – Parametri2.c (per indirizzo)

/* Parametri2.c : per indirizzo */
#include <stdio.h>

void Modifica (int *y) {
    *y += 2;
}

int main(void) {
    int x = 1;
    printf("%d \n", x);
    Modifica(&x);
    printf("%d \n", x);
    return 0;
}

Output: 1 e 3. La funzione ha modificato la cella di memoria di x.

Esempio combinato: uno per valore, uno per indirizzo

Programma C – Incrementa.c

/* Incrementa.c : per valore e per indirizzo */
#include <stdio.h>

void Aggiungi (int x, int *y) {
    x++;
    (*y)++;
    printf("Nella funzione chiamata: %d %d \n", x, *y);
    return;
}

int main(void) {
    int a = 0, b = 0;
    Aggiungi (a, &b);
    printf("Nella funzione principale: %d %d \n", a, b);
    return 0;
}

Output (ordine): 1 1 poi 0 1. a per valore (non cambia), b per indirizzo (cambia).

Quando usare cosa?

  • Per valore: la funzione non deve modificare i dati del chiamante (stampe, formattazioni, calcoli che ritornano un valore).
  • Per indirizzo: la funzione deve aggiornare le variabili del chiamante o restituire più risultati (scambi, accumuli, output multipli).

Mini-esercizi

1) Implementa swap(int *p, int *q) e usala per riordinare tre interi
Riusa swap in tre chiamate come in TreNumeri2.c.
2) Funzione che imposti max e min di tre numeri (parametri per indirizzo)
Leggi 3 interi, poi aggiorna *max e *min con confronti successivi.
3) Estendi Aggiungi per ritornare anche la somma via puntatore
Firma: void Aggiungi(int x, int *y, int *somma).

Torna all’indice ↑

Lezione 5 – Prototipi di funzione (dichiarazione vs definizione)

Le funzioni di un programma C possono comparire in qualsiasi ordine nel file sorgente, ma per poterle
usare in main() o in altre funzioni è necessario che il compilatore ne conosca la firma
(tipo di ritorno e lista dei parametri). Per questo si introducono i prototipi: dichiarazioni sintetiche poste in testa al programma
(subito dopo le #include e le eventuali variabili globali) che informano il compilatore su come verrà chiamata la funzione.
La definizione (il corpo con le istruzioni) può essere scritta più avanti, tipicamente dopo il main().

Perché usare i prototipi?

  • Sostengono l’approccio top-down: in alto l’“indice” (prototipi), sotto i dettagli (definizioni).
  • Permettono di chiamare funzioni definite più avanti o in altri file.
  • Abilitano il controllo dei tipi su argomenti e valore di ritorno alla chiamata.

Sintassi del prototipo

/* dichiarazione/prototipo */
tipo_ritorno nomeFunzione(tipo1 p1, tipo2 p2, ...);

/* definizione (implementazione) */
tipo_ritorno nomeFunzione(tipo1 p1, tipo2 p2, ...) {
    /* ... istruzioni ... */
    return valore; /* se non void */
}

Nei prototipi è possibile indicare solo i tipi senza i nomi dei parametri, ma specificare anche i nomi aiuta la leggibilità.

Esempio completo: area e perimetro di un quadrato (prototipi + definizioni)

Scomponiamo il problema in tre parti (input lato, calcolo area, calcolo perimetro) e dichiariamo i prototipi in testa al file.
Le variabili locali restano incapsulate nelle rispettive funzioni.

Programma C – Quadrato.c

/* Quadrato.c : perimetro e area del quadrato */
#include <stdio.h>

/* Prototipi (dichiarazioni) */
int  Tastiera(void);
void CalcoloArea (int l);
void CalcoloPerimetro (int l);

/* funzione principale */
int main(void) {
    int lato;
    lato = Tastiera();
    CalcoloArea(lato);
    CalcoloPerimetro(lato);
    return 0;
}

/* Definizioni (implementazioni) */
int Tastiera(void) {
    int misura; /* variabile locale */
    printf("Introduci il lato: ");
    scanf("%d", &misura);
    return misura;
}

void CalcoloArea (int l) {
    double area; /* variabile locale */
    area = (double)l * (double)l;
    printf("Area = %10.2f \n", area);
}

void CalcoloPerimetro (int l) {
    double perim; /* variabile locale */
    perim = l * 4.0;
    printf("Perimetro = %10.2f \n", perim);
}

Osservazioni didattiche:

  • I prototipi compaiono subito dopo le #include e prima del main().
  • Le funzioni non restitutive sono dichiarate void; quelle che restituiscono un valore specificano il tipo di ritorno.
  • Le variabili misura, area, perim sono locali alle rispettive funzioni.

Esempio: numero pari o dispari (uso del prototipo)

Programma C – PariDispari.c

/* PariDispari.c : numero pari o dispari */
#include <stdio.h>

/* Prototipo */
int Pari(int x);

/* funzione principale */
int main(void) {
    int y;
    printf("Un numero: ");
    scanf("%d", &y);
    if (Pari(y)) printf("%d numero pari \n", y);
    else         printf("%d numero dispari \n", y);
    return 0;
}

/* Definizione */
int Pari(int x) {
    return (x % 2 == 0) ? 1 : 0;
}

Dettaglio: l’istruzione if (Pari(y)) ... equivale a if (Pari(y) == 1) ..., ovvero “se la funzione restituisce vero (1)”.

Programmi multi-file e header

Nei progetti più grandi, funzioni e variabili possono essere distribuite su più file .c.
I prototipi delle funzioni (e le dichiarazioni condivise) si collocano in file di intestazione .h, inclusi con #include "miofile.h".
In compilazione si elencano tutti i .c necessari; il linker unirà gli oggetti in un eseguibile unico.

Buone pratiche con i prototipi:

  1. Raggruppa tutti i prototipi dopo le #include per avere una “mappa” del programma.
  2. Evita prototipi non coerenti con le definizioni (tipi e numero di parametri devono combaciare).
  3. Separa interfaccia (prototipi) e implementazione (corpi) per migliorare la leggibilità.

Mini-esercizi

1) Crea geometria.h con i prototipi e implementa in geometria.c
Prototipi: double areaQuadrato(int), double perimetroQuadrato(int).
2) Separa PariDispari.c in main.c, pari.c e pari.h
Includi "pari.h" nel main.c e compila con tutti i sorgenti.
3) Aggiungi inputInteroPositivo() con prototipo, definizione dopo main
Valida l’input ripetendo la lettura finché l’intero non è > 0.

Torna all’indice ↑

Lezione 6 – Funzioni predefinite (librerie standard C)

Il linguaggio C possiede alcune funzioni predefinite (built-in), che il programmatore può usare
nei programmi senza doverle implementare. Queste funzioni sono organizzate in librerie; per
richiamarle si inserisce all’inizio del file la direttiva #include con il relativo file di intestazione
(header) che contiene i prototipi delle funzioni.

Esempi di header usati spesso:

  • <stdio.h>: funzioni standard di input/output (es. printf, scanf);
  • <math.h>: funzioni matematiche (es. sqrt, pow, log);
  • <string.h>: funzioni per la manipolazione delle stringhe (es. strlen, strcmp);
  • <stdlib.h>: utilità varie (es. abs per interi, conversioni, allocazione dinamica…).

Esempio – Implementare (per esercizio) il valore assoluto di un double

Se la funzione fabs non fosse già disponibile, potremmo definirla così (solo a scopo didattico).
Per evitare conflitti di nome con la fabs di libreria, usiamo my_fabs.

Programma C – ValoreAssoluto.c

/* ValoreAssoluto.c : valore assoluto (versione didattica) */
#include <stdio.h>

double my_fabs(double x) {
    return (x < 0.0) ? -x : x;
}

int main(void) {
    double y;
    printf("Introduci un numero: ");
    if (scanf("%lf", &y) != 1) return 1;
    printf("%10.2f \n", my_fabs(y));
    return 0;
}

Per gli interi esiste la funzione abs(int) in <stdlib.h>.

Esempio – Calcolo della potenza (implementazione manuale)

Per esercizio, possiamo implementare l’elevamento a potenza moltiplicando la base per se stessa
un numero di volte pari all’esponente (intero e non negativo).

Programma C – PotInt.c

/* PotInt.c : potenza con esponente intero non negativo */
#include <stdio.h>

double pot_int(double base, int exp) {
    double p = 1.0;
    for (int i = 0; i < exp; ++i) p *= base;
    return p;
}

int main(void) {
    double base; int e;
    printf("Base: "); if (scanf("%lf", &base) != 1) return 1;
    printf("Esponente (intero >= 0): "); if (scanf("%d", &e) != 1 || e < 0) return 1;
    printf("Potenza = %lf \n", pot_int(base, e));
    return 0;
}

Limite didattico: questa implementazione considera il caso in cui l’esponente sia un intero positivo.

Esempio – Uso di pow da <math.h>

La funzione pow(base, esponente) accetta double e restituisce un double.

Programma C – PotInt2.c

/* PotInt2.c : potenza di un numero (pow) */
#include <stdio.h>
#include <math.h>

/* funzione principale */
int main(void) {
    double base, esponente;
    double risultato;

    printf("Base: ");
    if (scanf("%lf", &base) != 1) return 1;
    printf("Esponente: ");
    if (scanf("%lf", &esponente) != 1) return 1;

    risultato = pow(base, esponente);
    printf("Potenza = %lf \n", risultato);
    return 0;
}

Osservazione: pow è più completa e gestisce casi generali (esponenti reali, negativi, ecc.).
Compilazione con GCC: gcc PotInt2.c -o PotInt2 -lm.

Funzioni matematiche comuni (<math.h>)

  • pow(base, esponente) – elevamento a potenza (argomenti double).
  • exp(x) – calcola ex (numero di Nepero).
  • sqrt(x) – radice quadrata.
  • log(x) – logaritmo naturale (base e).
  • log10(x) – logaritmo in base 10.

Funzioni per stringhe (<string.h>)

  • strlen(s) – lunghezza della stringa s.
  • strcpy(dest, src) – copia src in dest.
  • strncpy(dest, src, n) – copia i primi n caratteri di src in dest.
  • strcmp(s1, s2) – confronto tra due stringhe (0 se uguali, <0 se s1<s2, >0 se s1>s2).
  • strcat(s1, s2) – concatenazione: appende s2 in coda a s1. Nota: s1 deve poter contenere il risultato completo.
Buone pratiche con gli header:
Includere gli header necessari prima delle funzioni che li richiedono. Gli header hanno “include guard” quindi le inclusioni ripetute non creano conflitti.

Mini-esercizi

1) Dati due double, stampa: pow(a,b), sqrt(|a|), log10(|b|+1)
Usa fabs di <math.h> per i valori assoluti; ricorda il link con -lm.
2) Funzione che rimuove gli spazi doppi da una stringa
Scorri la stringa e costruisci in-place o in un buffer di destinazione.
3) Reimplementa il valore assoluto usando fabs e, per interi, abs
Confronta i tipi (double vs int) e i relativi header.

Torna all’indice ↑