Il C e le macro.

Le macro semplici!:

 

La definizione di una macro semplice ha la forma:

#define identificatore elenco-di-sostituzione

L’elenco di sostituzione è una qualsiasi sequenza di token del processore.

L’elenco di sostituzione di una macro può contenere identificatori,keyword,costanti

numeriche,costanti carattere,stringhe letterali,operatori e caratteri di interpunzione.

Quando incontriamo la definizione di una macro il preprocessore prende nota del fatto

che l’identificatore rappresenta l’elenco di sostituzione. Ogni volta che nella parte

successiva del file viene incontrato l’identificatore,questo viene sostituito

con l’elenco di sostituzione.

CONSIGLIO:

/*******************************************************

Non mettere simboli aggiuntivi all’interno della definizione di una macro,quindi

diventerebbero parte della lista di sostituzione. Mettere il simbolo * nella

definizione di una macro è un errore piuttosto comune:

#define N = 100 /*** SBAGLIATO***/

int a[N]; /* diventa int a [= 100];*/

In questo esempio abbiamo erroneamente definito N come una coppia di token (= e 100).

Un altro errore frequente è quello di terminare la definizione di una macro con un

punto e virgola.

#define N = 100 /*** SBAGLIATO***/

int a[N]; /* diventa int a [100;];*/

Con questa definizione N corrisponde ai token 100 e ;.

Il compilatore si accorgerà della maggior parte degli errori causati da simboli

aggiunti nelle definizioni delle macro. Sfortunatamente il compilatore segnalerà

come un errore ogni utilizzo della macro invece di segnalare il vero colpevole

(la definizione della macro) che è stato rimosso dal preprocessore.

**************************************************************/

Le macro semplici sono utilizzate principalmente per definire le cosi dette

“costanti manifeste”. Utilizzando le macro possiamo assegnare nomi ai valori numerici

a caratteri e stringhe.

#define STR_LEN 80

#define TRUE 1

#define FALSE 0

#define PI 3,14159

#define CR ‘r’

#define EOS ”

#define MEM_ERR “Errore della memoria”

Utilizzare #define per assegnare dei nomi alla costanti ha diversi

vantaggi significativi

* Rende i programmi più facili da leggere: Il nome della macro (se scelto bene) aiuta

il lettore a comprendere il significato della costante. L’alternativa è un programma

pieno di “numeri magici” che possono disorientare facilmente il lettore.

* Rende i programmi più facili da modificare: Modificando la sola definizione della

macro possiamo cambiare il valore di una costante in tutto il programma.

Le costanti codificate “in modo fisso” sono molto più difficili da modificare,

soprattutto se qualche volta compaiono in una forma leggermente modificare (per esempio

un programma con un vettore di lunghezza 100 può avere un ciclo che va da 0 a 99.

Se cercassimo semplicemente le occorrenze di 100 all’interno del programma,

non troveremo il 99)

* Aiuta a evitare incosistenze ed errori tipografici: Se una costante numerica come

3.14159 comprare diverse volte, ci sono buone probabilità che venga scritta per errore

come 3.14116 o 3.14159.

Sebbene le macro semplici vengono utilizzate molto spesso per definire il nome delle

costanti,possono essere utilizzate anche per altri scopi.

* Effettuare dei piccoli combiamenti alla sintassi del C: In effetti possiamo alterare

la sintassi del C definendo delle macro da utilizzare come nomi alternativi per i

simboli del C. Per esempio i programmatori che preferiscono i token begin ed end

del Pascal alle parentesi { e } del C possono denire le seguenti macro:

#define BEGIN {

#define END }

* Rinominare i tipi: ad esempio creare un tipo booleano rinominando il tipo int:

#define BOOL int

anche se poco usato.

* Controllare la compilazione condizionale: Le macro giocano un ruolo importante

nella compilazione condizionale. Per esempio la presenza della seguente riga in un

programma può indicare che questo debba essere compilato nella “modalità di debug”

ovvero con istruzioni aggiuntive per produrre dell’output utile per il

debugging:

#define DEBUG

Per inciso possiamo dire che è possibile avere delle macro con l’elenco di

sostituzione vuoto cosi come vediamo nell’esempio appena presentato.

Di solito i programmatori C hanno l’abitudine di utilizzare solo lettere maiuscole

per i nomi delle macro che vengono utilizzate come costanti. Tuttavia non vi è

consenso su come scrivere le macro utilizzate per altri scopi. Dato che queste

(specialmente quelle parametriche) sono fonte di bache,ad alcuni programmatori piace

attirare l’attenzione su di esse utilizzando solo lettere maiuscole per i loro nomi.

Ma se si è abituati con le minuscole va benissimo non c’è una regola.

 

Le macro parametriche:

La definizione di una macro parametrica ha la seguente forma:

#define identificatore(x1,x2,…,xn) elenco-di-sostituzione

/************NOTA*****************

Non devono esserci spazi tra il nome della macro e la parentesi tonda sinistra. Se

viene lasciato dello spazio,il preprocessore penserà che si sta definendo una macro

semplice e tratterà (x1,x2,…,xn) come parte dell’elenco di sostituzione.

/*****************************************

Quando il prepocessore incontra una definizione di una macro parametrica,memorizza

la sua definizione per gli utilizzi successivi. Ogni volta che un invocazione della

macro della forma identificatore(y1,y2…,yn) comprare nel programma

(dove y1,y2,…,yn sono sequenze di token), il preprocessore la rimpiazza con

l’elenco di sostituzione rimpiazzando x1 con y1, x2 con y2 e cosi via..

Per esempio,supponete di aver definito le seguenti macro:

#define MAX(xy) ((x)>(y)?(x):(y))

#define IS_EVEN(n)  ((n)%2==0)

Anche se il numero di parentesi sembra alta c’è una ragione che vedremo in futuro..

Supponete ora di invocare le due macro in questo modo:

i 0 MAX(j+k, m-n)

if (IS_EVEN8i)) i++;

il preprocessore rimpiazzerà queste righe con:

i = ((j+k)>(m-n)?(j+k):(m-n))

if (((i)%2==0)) i++;

Come mostra questo esempio,spesso le macro parametriche fungono da semplici funzioni.

MAX si comporta come una funzione che calcola il maggiore tra due numeri.

IS_EVEN si comporta come una funzione che restituisce 1 se il suo argomento è un

numero pari,altrimenti restituisce 0.

Ecco una macro più complessa che si comprta come una funzione:

 

#define TOUPPER(c) (‘a’<=(c)&&(c)<=’z'?(c)-a’a'+’A':(c))

 

Questa macro controlla se il carattere c è compreso tra ‘a’ e ‘z’. Se è cosi produce

la versione maiuscola di c sottraendo ‘a’ e sommando ‘A’. Se non è cosi,la macro non

modifica c (in ctype.h contine una funzione di nome toupper che è più portabile.)

Una macro parametrica può avere un elenco di parametri vuoto. Ecco un esempio:

 

#define getchar() getc(stdin)

 

L’elenco vuoto di parametri non è necessario ma da in modo che getchar somigli a una

funzione (si è la stessa getchar che appartiene a <stdio.h> vedremo in futuro che di

solito la getchar è implementata come una macro oltre che come una funzione).

Utilizzare una macro parametri in luogo di una vere funzione presenta diversi vantaggi:

 

 

VANTAGGI:

 

* il programma può essere leggermente più veloce: Solitamente una chiamata a funzione

è causa di overhead durante l’esecuzione del programma(le informazioni sul contesto

devono essere salvate,gli argomenti devono essere copiati e cosi via). L’invocazione

di una macro,d’altro canto,non rchiede alcun overhead.

 

* Le macro sono “generiche”: I parametri delle macro a differenza dei parametri delle

funzioni, non possiedono un tipo particolare . Come risultato una macro può accettare

argomenti di qualsiasi tipo particolare. Come risultato una macro può accettare

argomenti di qualsiasi tipo, a patto che il programma risultante dopo il

preprocessamento sia valido. Per esempio possiamo utlizzare la macro MAX per trovare

il maggiore tra due valori di tipo int,long,float ecc..

 

SVANTAGGI:

 

*Il codice compilato spesso è di maggiori dimensioni: Ogni invocazione di macro provoca

l’inserimento dell’elenco di sostituzione e quindi l’incremento delle dimensioni del

sorgente del programma (e quindi l’incremento del codice compilato).

Più spesso viene utilizzata una macro e più l’effetto è pronunciato. Il problema

si aggrava quando le invocazioni delle macro vengono annidate.Considerate quello che

succede quando utilizziamo MAX presentata sopra per trovare il maggiore tra tre numeri:

 

n= MAX(i,MAX(j,k));

 

Ecco l’istruzione dopo il preprocessamento

 

n = ((i)>(((j)>(k)?(j):(k)))?(i):(((j)>(k)?(j):(k)));

 

* Non viene controllato il tipo degli argomenti: Quando una funzione viene invocata,

il compialtore controlla ogni argomento per vedere se è del tipo appropiato. Nel caso

non lo fosse, o l’argomento viene convertito nel tipo appropiato oppure il compilatore

produce un messaggio di errore. Gli argomenti delle macro non vengono controllati

dal preprocessore e quindi non vengono convertiti.

 

* non è possibile avere un puntatore a una macro: Come già visto in una guida fatta

da me il C permette puntatori a funzione, un concetto che è piuttosto utile in alcune

situazioni di programmazione. Le macro vengono rimosse durante il preprocessamento e

quindi non c’è un concetto di “puntatore a una macro”. Il risultato è che le macro non

possono essere utilizzate in queste situazioni.

 

* una macro può calcolare i suoi argomenti più volte: Una funzione calcola i suoi

argomenti solamente una volta. Una macro può calcolare i suoi argomenti due o più

volte. Calcolrare un argomento più di una volta può provocare un comportamento

inaspettato se l’argomento possiede dei side effect. Considerate cosa succede se uno

degli argomenti di MAX posside un side effect:

 

n = MAX(i++,j)

 

Ecco come si presenta la stessa riga dopo il preprocessamento:

 

n = ((i++)>(j)?(i++):(j));

 

Se i è maggiore di j allora i verrà (erroneamente) incrementata due volte e a n

verrà assegnato un valore non atteso.

 

/********ATTENZIONE********************************************

Gli errori quando un argomento di una macro viene calcolato più di una volta possono

essere difficili da trovare perchè l’invocazione della macro sembra uguale a una

chiamata a funzione. A peggiorare le cose c’è il fatto che una macro può funzionare

correttamente la maggior parte delle volte,creando problemi solo con certi argomenti

che possiedono dei side effect. Per prevenire inconvenienti è meglio evitare side

effect negli argomenti.

/***************************************************************

ART N 4:

[C] Macro: Macro parametriche pt 4:

 

Le macro parametriche non sono apprezzabili solo per la semplice simulazione delle

funzioni. In particolare, vengono utilizzate spesso come pattern di segmenti di codice

che noi stessi possiamo trovare ripetitivi. Supponete di annoiavi nello scrivere:

 

printf(“%dn”,i);

 

ogni volta che abbiamo bisogno di stampare un intero. Possiamo definire la seguente

macro che rende più facile la visualizzazione degli interi:

 

#define PRINT_INT(n) printf(“%dn”,n)

 

Una volta che PRINT_INT è stata definita, il preprocessore convertirà la riga

 

PRINT_INT(i/j);

 

in

 

printf(“%dn”, i/j);

 

 

Approfondimenti...

Vorresti inserire un commento?

Registrati per un account gratuito oppure entra (se sei già utente).





Mandaci un Trackback a questo URL se vuoi:
http://www.italianbloggers.it/52649/il-c-e-le-macro/trackback/