Skip to content

Compilatori e Portabilità

Per trasformare il codice sorgente (es. hello.c) in un programma eseguibile (es. a.out o hello.exe), hai bisogno di un Compilatore. I compilatori più diffusi per i sistemi Linux/macOS sono GCC (GNU Compiler Collection) e Clang, mentre su Windows si utilizza spesso Microsoft Visual Studio.

Ogni implementazione di un compilatore C differisce dall’altra per rispondere alle peculiarità della macchina bersaglio. Lo Standard C classifica i problemi di portabilità in categorie ben precise. Esaminiamo le tre più importanti che ogni programmatore C professionista deve conoscere:

1. Implementation-defined Behavior (Comportamento definito dall’implementazione)

Section titled “1. Implementation-defined Behavior (Comportamento definito dall’implementazione)”

Questo è un comportamento non fissato rigidamente dallo standard C. Tuttavia, il fornitore del compilatore deve scegliere, garantire e documentare un risultato coerente per quel comportamento specifico. Esempio: Il numero esatto di bit che compongono un intero (int). Su sistemi storici a 16-bit era di 2 byte (16 bit), mentre sui sistemi moderni a 32-bit e 64-bit è di 4 byte (32 bit).

2. Unspecified Behavior (Comportamento non specificato)

Section titled “2. Unspecified Behavior (Comportamento non specificato)”

Lo standard fornisce due o più opzioni valide su come eseguire una certa operazione, ma non obbliga il compilatore a dichiarare nel manuale quale sceglierà, e la scelta non deve nemmeno essere coerente tra esecuzioni successive. Esempio: L’ordine in cui vengono valutati gli argomenti di una funzione. Se chiami miaFunzione(eseguiA(), eseguiB()), non puoi assolutamente sapere se verrà prima chiamato eseguiA o eseguiB.

3. Undefined Behavior (UB - Comportamento non definito)

Section titled “3. Undefined Behavior (UB - Comportamento non definito)”

L’UB è il comportamento più insidioso, critico e temuto dello sviluppo in C. Avviene quando si infrangono le regole fondamentali del linguaggio tramite costrutti errati (ad es. divisione per zero, accedere oltre la fine di un array, usare puntatori invalidi).

Molti principianti credono che l’UB sia un difetto del linguaggio C. Al contrario, è una scelta progettuale estremamente intenzionale. Scaricando tutta la responsabilità di evitare gli errori a runtime (es. non ci sono controlli sui limiti degli array in C) sulle spalle del programmatore, il compilatore C non deve aggiungere istruzioni superflue di controllo, permettendo ottimizzazioni estreme e rendendo i binari C incredibilmente leggeri e veloci.

Questo esercizio mostra un comunissimo caso che varia in base al compilatore o all’architettura e rientra nell’Implementation-defined behavior: la dimensione in memoria (in byte) dei tipi primitivi. Usa la keyword sizeof per verificare.