Schema Generale di un File Header in Linguaggio C

Nelle lezioni precedenti abbiamo affrontato lo studio di diversi aspetti dei file header in linguaggio C:

  1. Ne abbiamo studiato lo scopo e l'utilizzo attraverso l'uso della direttiva #include;
  2. Ne abbiamo visto l'impiego per condividere tipi, costanti, variabili globali e funzioni tra più file sorgenti;
  3. Abbiamo studiato l'impiego dell'inclusione innestata e abbiamo affrontato il problema dell'inclusione multipla.

In base a queste conoscenze vedremo, adesso, uno schema generale di un file header in linguaggio C che può essere adoperato nella maggior parte dei casi.

Struttura generale di un file header

Quando si vuole creare un file header in linguaggio C, è importante seguire una struttura ben definita per facilitarne la lettura e la manutenzione.

Possiamo dividere un file header in diverse parti, ognuna delle quali contiene un tipo specifico di dichiarazioni:

  1. Guardie di inclusione: sono delle direttive di preprocessore che servono a evitare l'inclusione multipla dello stesso file header.

    Queste direttive sono composte da un blocco di codice che definisce una macro e due direttive #ifndef e #define che controllano se la macro è già stata definita. Se la macro non è stata definita, il blocco di codice viene eseguito e la macro viene definita. Se la macro è già stata definita, il blocco di codice non viene eseguito. Infine, alla fine del file header, viene definita la macro con la direttiva #endif.

    #ifndef NOME_FILE_H
    #define NOME_FILE_H
    
    /* Dichiarazioni */
    
    #endif
    
  2. Inclusioni: sono le direttive di preprocessore che includono le librerie necessarie per il file header.

    Un file header potrebbe, a sua volta, avere necessità di includere altri file header o librerie di sistema. Queste inclusioni vanno sempre prima delle altre dichiarazioni.

    #include <file_di_sistema_1.h>
    #include <file_di_sistema_2.h>
    #include "file_locale_1.h"
    #include "file_locale_2.h"
    
  3. Dichiarazioni di costanti: sono le dichiarazioni di costanti, come le costanti numeriche e le costanti di stringa.

    Le costanti vengono definite con la direttiva #define e vengono utilizzate per definire valori che non cambiano durante l'esecuzione del programma.

    #define COSTANTE_1 10
    #define COSTANTE_2 "costante"
    
  4. Dichiarazioni di tipi: sono le dichiarazioni di tipi personalizzati, come le strutture, le enumerazioni e i tipi definiti dall'utente.

    Queste dichiarazioni vengono utilizzate per definire nuovi tipi di dati che possono essere utilizzati in tutto il programma.

    typedef struct {
        int campo_1;
        char campo_2;
    } TipoStruttura;
    
    typedef enum {
        Valore_1,
        Valore_2,
        Valore_3
    } TipoEnumerazione;
    
  5. Dichiarazioni di variabili: sono le dichiarazioni di variabili globali.

    Le variabili globali vengono dichiarate con la parola chiave extern per indicare che sono definite in un altro file sorgente.

    extern int variabile_globale_1;
    extern char variabile_globale_2;
    
  6. Dichiarazioni di funzioni: sono le dichiarazioni di funzioni.

    Le funzioni vengono dichiarate con il loro prototipo, cioè con il loro nome, i tipi dei parametri e il tipo di ritorno.

    void funzione_1(int argomento_1, char argomento_2);
    void funzione_2(int argomento_1, char argomento_2);
    

Ovviamente, tutte queste parti sono opzionali. Per quanto riguarda, però, le guardie di inclusione, è sempre consigliabile includerle per evitare l'inclusione multipla del file header.

Di seguito è riportato uno schema generale di un file header in linguaggio C:

/* Parte 1: Guardie di Inclusione */
#ifndef NOME_FILE_H
#define NOME_FILE_H

/* Parte 2: Inclusioni */
#include <file_di_sistema_1.h>
#include <file_di_sistema_2.h>
#include "file_locale_1.h"
#include "file_locale_2.h"

/* Parte 3: Dichiarazioni di Tipi */
typedef struct {
    int campo_1;
    char campo_2;
} TipoStruttura;

typedef enum {
    Valore_1,
    Valore_2,
    Valore_3
} TipoEnumerazione;

/* Parte 4: Dichiarazioni di Costanti */
#define COSTANTE_1 10
#define COSTANTE_2 "costante"

/* Parte 5: Dichiarazioni di Variabili */
extern int variabile_globale_1;
extern char variabile_globale_2;

/* Parte 6: Dichiarazioni di Funzioni */
void funzione_1(int argomento_1, char argomento_2);
void funzione_2(int argomento_1, char argomento_2);

/* Parte 7: Fine Guardie di Inclusione */
#endif

Questo schema di file header va bene nella maggior parte dei casi, ma può variare a seconda delle esigenze del progetto. Ad esempio, se il file header è molto grande, potrebbe essere utile suddividerlo in più file header per facilitarne la manutenzione.