Anatomia di un programma in Linguaggio C

Entriamo nel dettaglio di come è strutturato un programma in linguaggio C. Vedremo le direttive, le funzioni e le istruzioni che compongono un programma in C.

La forma generale di un Programma

Riprendiamo, in questa lezione, il primo semplice programma di esempio che abbiamo visto nella lezione precedente: ciao.c.

#include <stdio.h>

int main() {
    printf("Ciao, Mondo!\n");
    return 0;
}

Possiamo generalizzare la struttura di questo programma in questo modo:

direttive

int main() {
    istruzioni
}

Analizziamo questo schema. La prima cosa da notare è che il corpo del programma, ossia il main, è delimitato da due parentesi graffe che ne indicano l'inizio e la fine.

In linguaggio C, le parentesi graffe { e } sono utilizzate allo stesso modo in cui altri linguaggi utilizzano parole chiavi come begin e end. Ciò delinea come il linguaggio C sia un linguaggio molto sintetico e conciso. Esso fa molto uso di simboli speciali e abbreviazioni per ridurre la quantità di codice necessaria per scrivere un programma. Questo può essere visto come un punto di forza, ma può essere anche considerato, specialmente da chi è alle prime armi, come un punto di debolezza in quanto rende il codice criptico.

Detto questo, la struttura di un qualunque programma C, anche il più semplice, può essere ricondotta a tre caratteristiche fondamentali:

  • direttive: comandi di modifica del codice sorgente applicati prima della compilazione vera e propria;
  • funzioni: blocchi di codice che eseguono un insieme di azioni specifiche, di cui la funzione main ne è un esempio;
  • istruzioni: comandi da eseguire nel momento in cui il programma è in esecuzione.

Il linguaggio C è, infatti, un linguaggio imperativo e procedurale:

  • Linguaggio imperativo indica un linguaggio che esegue tutte le istruzioni di cui è composto un programma in sequenza, una dopo l'altra;
  • Linguaggio procedurale indica un linguaggio che organizza il codice in funzioni, ossia blocchi di codice che eseguono un insieme di azioni specifiche.

Esaminiamo, adesso, queste tre caratteristiche nel dettaglio.

Direttive

Come già accennato nella lezione precedente, un file sorgente scritto in C, prima di essere compilato, viene pre-processato. Ciò significa che viene prima modificato da un programma chiamato preprocessore.

I comandi specifici del preprocessore prendono il nome di direttive di precompilazione. Studieremo nel dettaglio queste direttive nel capitolo legato al preprocessore. Per ora, accenneremo al funzionamento di una direttiva particolare molto importante: la direttiva #include.

Il programma di esempio, ciao.c, inizia, infatti, con la riga che segue:

#include <stdio.h>

Questa direttiva indica al precompilatore che le informazioni contenute nel file stdio.h devono essere incluse nel programma prima che esso possa essere compilato. Questo file, stdio.h, contiene informazioni riguardanti la libreria di Input e Output del C.

Un compilatore per linguaggio C, infatti, mette a disposizione dello sviluppatore anche una libreria standard del C. Si parla di una libreria, in quanto ne esistono implementazioni diverse, ma tutte con lo stesso scopo: fornire un insieme di funzioni e strutture dati che permettono di scrivere programmi in C portabili tra sistemi diversi.

Associati alla libreria, esistono dei file header (file di intestazione in italiano) che contengono le informazioni necessarie a poter utilizzare le funzioni di libreria. Questi file hanno, tipicamente, l'estensione .h e sono inclusi nel programma tramite la direttiva #include.

La ragione per cui inseriamo la riga di sopra è che il C non ha, come invece hanno altri linguaggi, funzioni di Input e Output built-in. Per cui, un programma qualunque deve far affidamento alla libreria standard per poter comunicare con l'ambiente esterno.

Definizione

Direttive di Precompilazione

Le direttive di precompilazione sono comandi che vengono eseguiti prima della compilazione vera e propria del programma. Questi comandi sono preceduti dal simbolo # e vengono utilizzati per modificare il codice sorgente in modo da renderlo compilabile.

Di base, tutti i programmi C che intendono leggere e scrivere da console devono includere la libreria stdio.h tramite la direttiva #include <stdio.h>.

Le direttive cominciano sempre con il cancelletto # proprio per distinguerle dal resto delle istruzioni del programma e non terminano mai con un punto e virgola ;.

Per ora è sufficiente sapere questo per poter iniziare a programmare in C. Nelle prossime lezioni, poi, approfondiremo il funzionamento delle direttive di precompilazione e delle altre direttive più importanti.

Funzioni

In C, le funzioni sono come le procedure o le subroutine di altri linguaggi di programmazione. In altre parole, sono i blocchi costruttivi di base con cui si realizzano i programmi.

Dato che il linguaggio C è un linguaggio procedurale, un programma non è altro che una collezione di funzioni o procedure che si chiamano a vicenda per eseguire un insieme di azioni specifiche.

Possiamo, a grandi linee, dividere le funzioni in due categorie:

  • Le funzioni scritte dall'utente: sono funzioni che l'utente scrive per realizzare un insieme di azioni specifiche;
  • Le funzioni della libreria standard: sono funzioni che fanno parte della libreria standard del C e che l'utente può utilizzare.

Il termine funzione deriva dalla matematica, dove una funzione è una regola per calcolare un risultato a partire da un insieme di argomenti. Ad esempio, funzioni matematiche sono:

f(x, y) = \sqrt{x^2 + y^2}
g(x) = x^2 + 2x + 1

In linguaggio C una funzione è un concetto leggermente diverso da quello di funzione matematica. Infatti, una funzione in C può essere considerata semplicemente come un'aggregazione di istruzioni che devono essere eseguite in sequenza. Inoltre, una funzione C può anche non restituire alcun risultato.

Nel caso in cui una funzione debba restituire un risultato, si può adoperare l'istruzione return per restituire un valore.

Per il momento non ci concentreremo sulle funzioni che studieremo in un capitolo successivo nel dettaglio.

Quello che è importante sapere è che, sebbene un programma C può essere costituito da un insieme di funzioni qualunque, esso deve obbligatoriamente contenere la funzione main.

La funzione main è, infatti, speciale. Essa rappresenta il cosiddetto entry point (punto di ingresso in italiano), ossia il punto di partenza del programma. Quando un programma C viene eseguito, il sistema operativo cerca la funzione main e la esegue.

Per il momento, finquando non arriveremo al capitolo sulle funzioni, la funzione main sarà l'unica funzione dei nostri programmi.

Definizione

Funzione main

La funzione main è la funzione principale di un programma C. Essa rappresenta il punto di partenza del programma. Un qualunque programma C deve contenere la funzione main.

Nota

Il nome main è fondamentale

Il linguaggio C è case-sensitive, ossia fa distinzione tra maiuscole e minuscole. Per cui, il nome della funzione main deve essere scritto esattamente come main e non come Main, MAIN, mAIN, ecc.

Dal momento che main è una funzione, essa può avere un valore di ritorno. Se ritorniamo all'esempio del programma ciao.c vediamo che verso la fine della funzione main c'è l'istruzione seguente:

return 0;

Ossia la funzione main restituisce in uscita un numero intero ed in particolare il numero 0. Il fatto che essa restituisca un intero è evidenziato dal fatto che abbiamo scritto la funzione main in questo modo:

int main() {

In altre parole, stiamo dicendo al compilatore che la funzione main restituirà un valore intero int. Vedremo nel dettaglio in futuro come indicare il tipo dei valori restituiti dalla funzione.

Quello che dobbiamo sapere è che la funzione main deve restituire in uscita un numero intero.

Quello che ci chiediamo adesso è: perché proprio il numero 0?

Quando il programma raggiunge l'istruzione return 0; accadono due cose:

  1. Il programma termina;
  2. Viene inviato al sistema operativo il numero 0.

Quando il sistema operativo riceve il numero 0, esso lo interpreta come segnale che il programma è terminato correttamente. Se il programma terminasse con un numero diverso da 0, il sistema operativo interpretarebbe ciò come il fatto che il programma abbia incontrato un errore. Questa convenzione è nata in ambiente UNIX ed è tuttora seguita da molti sistemi operativi moderni, tra cui Linux, Windows e macOS.

Se dovessimo dimenticare di inserire l'istruzione return 0; alla fine della funzione main, la maggior parte dei compilatori segnalerebbe un warning.

Istruzioni

Un'istruzione è un comando che viene eseguito dal programma quando quest'ultimo è in esecuzione.

Dal momento che il linguaggio C è un linguaggio imperativo, un programma C esegue tutte le istruzioni in maniera sequenziale, ossia eseguendo le istruzioni una dopo l'altra nell'ordine in cui si presentano.

Analizzeremo nel dettaglio le istruzioni del linguaggio nelle prossime lezioni. Per il momento soffermiamoci sul nostro primo programma ciao.c.

Se lo osserviamo bene, notiamo che esso è composto da due sole istruzioni:

printf("Ciao, Mondo!\n");
return 0;

La seconda istruzione l'abbiamo già incontrata sopra. Essa restituisce il valore 0 alla fine del programma ed indica al sistema operativo che il programma è terminato correttamente.

La prima istruzione, invece, è una chiamata a funzione. Quando si vuole utilizzare una funzione per uno specifico compito, in gergo tecnico, si diche che si chiama o invoca la funzione. Nel nostro caso, stiamo chiamando la funzione printf per stampare un messaggio a schermo.

Una regola fondamentale del C è che ogni istruzione deve terminare con un punto e virgola ;. Il punto e virgola indica al compilatore che un'istruzione è terminata ed è molto importante perché possiamo scrivere le istruzioni suddividendole su più righe.

Solo le direttive non terminano con un punto e virgola e non possono essere suddivise su più righe.

Esistono delle eccezioni al punto e virgola, in particolare possiamo scrivere blocchi di codice, ma le studieremo più avanti.

Definizione

Istruzioni

Un'istruzione è un comando che viene eseguito dal programma quando quest'ultimo è in esecuzione. Ogni istruzione deve terminare con un punto e virgola ;.

Stampa di messaggi

Concludiamo questa lezione con un'introduzione sull'utilizzo della funzione printf. Anticipiamo qui alcune nozioni, che studieremo in dettaglio in una lezione dedicata, anche perché è fondamentale saperla usare fin da subito.

La funzione printf può essere adoperata per stampare a schermo dei messaggi. Osservando il programma di esempio ciao.c, vediamo che la funzione printf è stata utilizzata in questo modo:

printf("Ciao, Mondo!\n");

Quando viene eseguita, la funzione printf stampa il messaggio Ciao, Mondo! a schermo ma senza gli apici. Questo perché gli apici indicano al compilatore che il testo racchiuso al loro interno è una stringa letterale.

Le stringhe, in linguaggio C, sono sequenze di caratteri racchiuse tra apici.

Un importante dettaglio sul messaggio passato alla funzione printf riguarda la sequenza \n. Normalmente la funzione printf non va a capo alla fine del messaggio. Per andare a capo, dobbiamo inserire la sequenza \n che rappresenta il carattere di nuova riga.

Le sequenze che cominciano per \ sono chiamate sequenze di escape e sono utilizzate per rappresentare caratteri speciali. Ad esempio, la sequenza \n rappresenta il carattere di nuova riga, \t rappresenta il carattere di tabulazione, ecc.

Avremmo potuto, ad esempio, scrivere il messaggio su più righe in questo modo:

printf("Ciao,\nMondo!\n");

Ottenendo un risultato del genere:

Ciao,
Mondo!

Analogamente, avremmo anche potuto spezzare un unico messaggio in due ma senza andare a capo:

printf("Ciao, ");
printf("Mondo!\n");

Ottenendo il messaggio:

Ciao, Mondo!

Per ora, questo è quello che ci serve conoscere per la funzione printf. Studieremo in dettaglio questa funzione in una lezione dedicata.

In Sintesi

In questa lezione abbiamo visto la struttura generale di un programma in linguaggio C. Abbiamo visto che un programma C è composto da tre elementi fondamentali:

  • direttive: comandi di precompilazione che vengono eseguiti prima della compilazione del programma;
  • funzioni: blocchi di codice che eseguono un insieme di azioni specifiche;
  • istruzioni: comandi che vengono eseguiti quando il programma è in esecuzione.

Abbiamo visto che la funzione main è la funzione principale di un programma C e che essa deve restituire in uscita un numero intero. Abbiamo visto che il numero 0 indica al sistema operativo che il programma è terminato correttamente.

Abbiamo visto, infine, come utilizzare la funzione printf per stampare messaggi a schermo.

Nella prossima lezione studieremo un altro importante elemento costituente dei programmi scritti in C: i commenti.