PROGRAMMIAMO
C++ - Le direttive del preprocessore
Il preprocessore

In C++ (come in C), prima che il compilatore inizi a tradurre un file sorgente, viene attivato un programma, detto preprocessore. Come suggerisce il nome, il preprocessore effettua un'elaborazione preliminare del file sorgente, prima della compilazione vera e propria.

In pratica il preprocessore ricerca nel file sorgente speciali istruzioni, chiamate direttive. Una direttiva inizia sempre con il carattere #  e occupa una sola riga. Alcuni esempi di direttive sono:

#include <cstdlib>
#include <iostream>

#define PIGRECO 3.14159

Il file sorgente contenente le direttive, viene tradotto dal preprocessore in quella che viene chiamata la translation unit. In pratica una translation unit è un file sorgente (che deve dunque ancora essere compilato) in cui le direttive sono state sostituite dal codice vero e proprio.

La direttiva #define e le costanti

La direttiva #define consente di assegnare un nome a una costante. Ad esempio:

#define PIGRECO 3.14159

Questo significa che il preprocessore sostituisce ogni occorrenza della parola PIGRECO all'interno del programma col valore 3.14159. Per esempio i seguenti due programmi sono del tutto equivalenti in C++:

#include <cstdlib>
#include <iostream>

using namespace std;

#define PIGRECO 3.14159

int main(int argc, char *argv[])
{
double raggio, perimetro;
cout<<"Inserire raggio del cerchio: ";
cin>>raggio;
perimetro=2*PIGRECO*raggio;
cout<<"Il perimetro vale "<<perimetro;
system("PAUSE");
return EXIT_SUCCESS;
}
#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
double raggio, perimetro;
cout<<"Inserire raggio del cerchio: ";
cin>>raggio;
perimetro=2*3.14159*raggio;
cout<<"Il perimetro vale "<<perimetro;
system("PAUSE");
return EXIT_SUCCESS;
}

La sintassi della direttiva #define è la seguente

#define nome valore

Il nome della costante viene di solito scritto in maiuscolo per distinguerlo dalle variabili, ma non è obbligatorio. Di solito la direttiva #define viene scritta all'inizio del file, fuori dal main, ma può essere inserita ovunque, purché prima dell'uso effettivo della costante stessa.

Definire le costanti è utile in quanto consente di modificare rapidamente e senza possibile dimenticanze un valore ripetuto più volte in un programma. Si consideri per esempio il caso seguente:

#include <cstdlib>
#include <iostream>

using namespace std;

#define MAXDIM 10

int main(int argc, char *argv[])
{
int numeri[MAXDIM], i;

cout<<"Inserisci "<<MAXDIM<<" numeri interi:\n";

for (i=0; i<MAXDIM; i++)
    cin>>numeri[i];

system("PAUSE");
return EXIT_SUCCESS;
}

Se si vuole cambiare le dimensioni del vettore, è sufficiente modificare la definizione della costante MAXDIM e automaticamente il cambiamento viene effettuato in tutto il programma.

La direttiva #define e le macro

Un modo più interessante di usare la direttiva #define è quello che consente di definire una macro. Una macro in C++ è un piccolo frammento di codice con un nome e dei parametri. Per esempio:

#include <cstdlib>
#include <iostream>

using namespace std;

#define quadrato(a) a*a

int main(int argc, char *argv[])
{
int numero, ris;

cout<<"Inserisci un numero: ";
cin>>numero;

ris = quadrato(numero);

cout<<"Il quadrato di "<<numero<<" vale "<<ris<<"\n";
system("PAUSE");
return EXIT_SUCCESS;
}

Si osservi la definizione della macro:

#define quadrato(a) a*a

Il nome della macro è quadrato. Si notino le parentesi tonde dopo il nome e il parametro a (nome scelto liberamente) chiuso fra le parentesi. Il preprocessore in pratica espande

ris = quadrato(numero);

facendolo diventare

ris = numero*numero;

Le macro assomigliano molto alle funzioni, ma, a differenza di queste, non sono sottoprogrammi autonomi che vengono chiamati. Si tratta semplicemente di un meccanismo di sostituzione attuato dal preprocessore prima della compilazione vera e propria del programma.

Le macro sono generalmente più veloci delle funzioni (proprio perché non richiedono chiamata) ma presentano qualche rischio. Si consideri il seguente esempio:

#include <cstdlib>
#include <iostream>

using namespace std;

#define quadrato(a) a*a

int main(int argc, char *argv[])
{
int numero, ris;

cout<<"Inserisci un numero: ";
cin>>numero;

ris = quadrato(numero+1);

cout<<"Il quadrato di "<<numero+1<<" vale "<<ris<<"\n";
system("PAUSE");
return EXIT_SUCCESS;
}

L'esempio non fornisce il risultato corretto. Per esempio inserendo il valore 2 si ottiene come risultato che il quadrato di 3 è 5! Il motivo è che la riga contenente la macro viene espansa letteralmente nel seguente modo:

ris = numero+1*numero+1;

A causa della mancanza di parentesi e dell'ordine di precedenza sbagliato, il risultato che si ottiene è esso pure sbagliato. In questo caso il problema può essere risolto definendo la macro in questo modo:

#define quadrato(a) (a)*(a)

 

La direttiva #include

Abbiamo già incontrato molte volte l'uso di questa direttiva, come per esempio in:

#include <cstdlib>

Il significato è molto semplice: il contenuto del file di testo di nome cstdlib viene copiato e incollato (incluso) nel punto in cui compare la direttiva all'interno del file sorgente. Il file si trova all'interno di una sottocartella all'interno della cartella di installazione di Dev-C++ (il percorso standard per i file include in C++ è Dev-Cpp/include e la sottocartella c++/3.4.2 dove l'ultimo numero indica la versione del programma).

I file include sono usati per contenere la definizione dei prototipi, delle macro, dei tipi e delle costanti utilizzate nelle funzioni di library del C++. Per esempio aprendo con un editor di testi il file math.h utilizzato per le funzioni della library matematica troviamo, fra le altre cose:

#define M_LOG2E 1.4426950408889634074
#define M_LOG10E 0.43429448190325182765
#define M_LN2 0.69314718055994530942
#define M_LN10 2.30258509299404568402
#define M_PI 3.14159265358979323846
#define M_PI_2 1.57079632679489661923
#define M_PI_4 0.78539816339744830962
#define M_1_PI 0.31830988618379067154
#define M_2_PI 0.63661977236758134308
#define M_2_SQRTPI 1.12837916709551257390
#define M_SQRT2 1.41421356237309504880
#define M_SQRT1_2 0.70710678118654752440

Qui possiamo riconoscere la definizione di una serie di costanti matematiche usate dalle funzioni. Si osservi a questo proposito che un file include può contenere al proprio interno altre direttive!

La direttiva #include può essere usata anche in questo modo:

#include "nomefile"

In questo caso il file di nomefile scritto fra virgolette viene cercato nella cartella corrente del programma (e non nella cartella di sistema). Quest'uso può essere utile quando si voglia scrivere un proprio file include da usare insieme a un dato programma (per esempio per includere rapidamente in testa a un programma tutti i prototipi delle funzioni chiamate e tutte le costanti usate dal programma stesso).

Le direttive condizionali

Le direttive condizionali servono per fare compilare certe parti del programma in dipendenza dal verificarsi o meno di determinate condizioni. Per esempio:

#define COUNTRY ITALY

 

#if COUNTRY==ITALY

   char moneta[]="euro";

#elif CONTRY==US

   char moneta[]="dollar";

#else

   char moneta[]="pound";

#endif

In questo caso, a seconda del valore della costante COUNTRY, il vettore di caratteri moneta viene inizializzato con un valore diverso.

Senza voler approfondire troppo l'argomento, che è piuttosto complesso, segnaliamo qui un uso frequente delle direttive condizionali allo scopo di commentare una porzione di programma. Spesso quando si testa un programma si ha bisogno di "eliminare" temporaneamente parti del programma, in modo da verificare il funzionamento del resto.

Di solito questa cancellazione temporanea può essere fatta commentando la porzione di codice da eliminare, come nel seguente esempio:

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
    {

    int divisore, numero, resto;

    cout<<"Fornisci un numero: ";
    cin>> numero;

    divisore=2;

/*    while (divisore<numero)
        {
        resto = numero%divisore;

        if (resto==0)
            {
            cout<<"Il numero "<<numero<<" non e' primo\n";
            break;
            }

        divisore = divisore+1;
        }
*/

    if (divisore == numero)  /* se il ciclo è arrivato a conclusione */
        cout<<"Il numero "<<numero<<" e' primo\n";

    system("PAUSE");
    return EXIT_SUCCESS;
    }

La parte commentata (in rosso) è stata temporaneamente eliminata dal programma. Il problema di questa soluzione è che i commenti non possono contenere al proprio interno altri commenti. In altre parole, non si può eliminare con un commento una porzione di programma che già contenga un commento. Si consideri l'esempio che segue:

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
    {

    int divisore, numero, resto;

    cout<<"Fornisci un numero: ";
    cin>> numero;

    divisore=2;

    while (divisore<numero)
        {
        resto = numero%divisore;

        if (resto==0)
            {
            cout<<"Il numero "<<numero<<" non e' primo\n";
            break;
            }

        divisore = divisore+1;
        }

/*    if (divisore == numero)  /* se il ciclo è arrivato a conclusione */
        cout<<"Il numero "<<numero<<" e' primo\n";
*/


    system("PAUSE");
    return EXIT_SUCCESS;
    }

Il commento indicato in rosso non viene accettato in quanto contiene al proprio interno un altro commento e dunque non può essere interpretato correttamente dal compilatore.

In questi casi si adotta in genere la seguente soluzione:

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
    {

    int divisore, numero, resto;

    cout<<"Fornisci un numero: ";
    cin>> numero;

    divisore=2;

    while (divisore<numero)
        {
        resto = numero%divisore;

        if (resto==0)
            {
            cout<<"Il numero "<<numero<<" non e' primo\n";
            break;
            }

        divisore = divisore+1;
        }

#ifdef NODEF
     if (divisore == numero)  /* se il ciclo è arrivato a conclusione */
        cout<<"Il numero "<<numero<<" e' primo\n";
#endif


    system("PAUSE");
    return EXIT_SUCCESS;
    }

NODEF è un nome (scelto a caso, ma in modo esplicativo) di una costante che non è definita da nessuna parte nel programma. La direttiva condizionale #ifdef specifica che quella parte di programma dev'essere compilata solo se è definita la costante NODEF. Ma dal momento che questa costante non è stata definita, il risultato pratico è quello di escludere dalla compilazione la parte racchiusa fra #ifdef e #endif.

 

link precedente - successiva

Sito realizzato in base al template offerto da

http://www.graphixmania.it