Sei sulla pagina 1di 28

Corso di Fondamenti di Informatica

Ingegneria delle Comunicazioni – BCOR


Ingegneria Elettronica – BELR

Introduzione al C

Unità 10 – Preprocessore

D. Bloisi, A. Pennisi, S. Peluso, S. Salza


Processo di compilazione
Codice
sorgente
Preprocessore (e.g. cpp)

Codice sorgente
preprocessato

Compilatore (e.g. gcc)

Codice
Eseguibile
oggetto
Linker (e.g. ld)
Preprocessore - Unità 10 2013/2014 Pagina 2
Preprocessing
Il preprocessore ha il compito di effettuare
sostituzioni testuali sul codice sorgente di un
programma (preprocessing).

Tali sostituzioni vengono effettuate sul


contenuto del file sorgente prima della
compilazione vera e propria.

Ogni sorgente C viene preprocessato.

Preprocessore - Unità 10 2013/2014 Pagina 3


Preprocessore del C
Il C è un linguaggio di programmazione dotato
di un preprocessore standard detto cpp.

In molte implementazioni, è un programma


separato che viene invocato dal compilatore
come primo passo di compilazione

Non tutti i linguaggi di programmazione hanno


necessità di usare preprocessori.
Per esempio, il linguaggio Java non ha un
preprocessore.
Preprocessore - Unità 10 2013/2014 Pagina 4
Direttive del preprocessore
Il preprocessore gestisce direttive per

•  Inclusione di file sorgenti (#include)

•  Definizione di costanti simboliche e macro


(#define)

•  Compilazione condizionale (e.g. #if)

Tutte le direttive del preprocessore


incominciano con #
Preprocessore - Unità 10 2013/2014 Pagina 5
La direttiva #include
La direttiva del preprocessore #include consente di
includere in sua vece la copia di un file specificato.
Si tratta di una vera e propria sostituzione tipografica,
che permette di modificare il sorgente con l’aggiunta dei
dati contenuti nel file incluso.

Ad esempio, l’inclusione dei file di header serve a poter


accedere ai prototipi delle funzioni definiti in quei file,
come per
#include <math.h>

Preprocessore - Unità 10 2013/2014 Pagina 6


Forme per #include
Esistono due forme della direttiva #include

Ø  #include <nomefile>

Ø  #include "nomefile"

Nel primo caso il preprocessore ricercherà il file


nelle directory di sistema predefinite, mentre nel
secondo caso eseguirà la ricerca del file da
includere nella stessa directory in cui si trova il
file da compilare.
Preprocessore - Unità 10 2013/2014 Pagina 7
La direttiva #define

La direttiva #define serve per definire

•  Costanti simboliche, cioè costanti


rappresentate da simboli

•  Macro, cioè operazioni definite come simboli

Preprocessore - Unità 10 2013/2014 Pagina 8


Sintassi
Il formato della direttiva #define è

#define identificatore testo-di-sostituzione

Esempio

#define E 2.718281

Preprocessore - Unità 10 2013/2014 Pagina 9


Semantica
Dal momento in cui la direttiva #define
compare in un file, tutte le successive
occorrenze di identificatore che non sono
racchiuse tra doppi apici sono sostituite da
testo-da-sostituire

#define TRUE 1
#define FALSE 0
...
int finito = FALSE;
while( finito != TRUE ) { ... }
Preprocessore - Unità 10 2013/2014 Pagina 10
Uso di #define
• Si usa per convenzione scrivere le costanti
definite tramite preprocessore in maiuscolo.

• Questo permette di renderle subito


riconoscibili leggendo il testo del
programma, in modo da non confonderle
con le variabili.

• La #define può essere scritta su più linee


successive se l’ultimo carattere della linea
è ‘\‘ (backslash) ed è seguito da un ritorno a
capo sulla linea successiva.
Preprocessore - Unità 10 2013/2014 Pagina 11
Errore tipico
#include <stdio.h>
#define E = 2.718281

int main() {
printf("%f\n", E);
}

esempiodefine.c: In function 'main':


esempiodefine.c:5:17: error: expected
expression before '=' token

Preprocessore - Unità 10 2013/2014 Pagina 12


Codice corretto
#include <stdio.h>
#define E 2.718281

int main() {
printf("%f\n", E);
}

Output
2.718281

Preprocessore - Unità 10 2013/2014 Pagina 13


Macro
Una macro è un’operazione definita all’interno di
una direttiva #define

A differenza delle costanti simboliche possono


essere definite con argomenti

#define AREA_CERCHIO(x) (3.14 * (x) *


(x))
oppure
#define AREA_CERCHIO(x) \
(3.14 * (x) * (x))
Preprocessore - Unità 10 2013/2014 Pagina 14
Esempio

#include <stdio.h>

#define AREA_CERCHIO(x) \
(3.14 * (x) * (x))

int main() {
int r = 3;
printf("%f\n", AREA_CERCHIO(r + 2));
return 0;
}
Preprocessore - Unità 10 2013/2014 Pagina 15
Vantaggi delle macro
Le macro hanno dei vantaggi rispetto alle
funzioni, poiché il codice definito in esse viene
direttamente inserito nel programma evitando
quindi il costo di una chiamata a funzione.

Inoltre, l’uso di macro permette comunque di


mantenere leggibile il codice.

Preprocessore - Unità 10 2013/2014 Pagina 16


Cautela con le macro
L’utilizzo delle macro si presta facilmente ad
errori.

#define QUADRATO(a) a*a

Il codice
QUADRATO(1+2)
diventa “1+2*1+2” con risultato 5 invece di 9

Soluzione
#define QUADRATO(a) ((a)*(a))
Preprocessore - Unità 10 2013/2014 Pagina 17
Direttiva #undef
La direttiva #undef rimuove la definizione
corrente relativa all’identificatore (costante
simbolica o macro) che la segue.

La visibilità di una costante simbolica (e di una


macro) si estende dalla sua definizione fino al
relativo #undef o alla fine del file.

Preprocessore - Unità 10 2013/2014 Pagina 18


Le direttive #if e #ifdef
Utilizzando la compilazione condizionale
(direttive #if e #ifdef) si possono introdurre
segmenti di codice in dipendenza da
particolari condizioni.

Sono simili alla struttura di selezione if

Ognuna delle direttive condizionali sarà


valutata come una espressione intera costante

Preprocessore - Unità 10 2013/2014 Pagina 19


Esempio

#if espr-costante-intera
/*
* codice che sara’ eseguito
* se espr-costante-intera è vera
*/
# endif
Tutti i caratteri compresi tra #if e #endif
sono inclusi nel file da passare al compilatore
solo se l’espressione è diversa da 0

Preprocessore - Unità 10 2013/2014 Pagina 20


Esempio
# ifdef DEBUG
/* Viene valutato se il
* codice simbolo DEBUG è
*/ definito (nel senso di
# endif #define) o meno
# ifndef DEBUG
/*
* codice
*/
# endif
Preprocessore - Unità 10 2013/2014 Pagina 21
Esempio pratico
#include<stdio.h>
int main(){
#ifdef __LINUX__
printf("Sistema Operativo: Linux\n");

#elif __APPLE__
printf("Sistema Operativo: Mac OS o iOS\n");

#elif _WIN32 || _WIN64 || __CYGWIN__ || __CYGWIN32__


printf("Sistema Operativo: Windows\n”);

#endif
return 0;
}

Preprocessore - Unità 10 2013/2014 Pagina 22


Importante
E’ possibile utilizzare la direttiva #if per
eliminare porzioni di codice senza cancellarle
(per esempio, in fase di debug )

#if 0
codice da
non considerare
# endif

Preprocessore - Unità 10 2013/2014 Pagina 23


L’utilizzo di #if in debug
/*
int funzione ()
{
/*
commenti questi commenti
nella creano problemi se
funzione si tenta di
*/ commentare TUTTA
} la funzione usando
*/ /* */

Preprocessore - Unità 10 2013/2014 Pagina 24


L’utilizzo di #if in debug
#if 0
int funzione ()
{
/*
commenti questi commenti non
nella creano problemi se
funzione si
*/ utilizza #if
}
#endif
Preprocessore - Unità 10 2013/2014 Pagina 25
Esempio completo su DEBUG (1/3)
#include <stdio.h>
#include <math.h>

int main(){
double input, output;
printf("Inserire un numero positivo\n");
scanf("%lf", &input);

if(input <= 0){


printf("Errore: input non posivito\n");
return -1;
}

output = sqrt(input);

Preprocessore - Unità 10 2013/2014 Pagina 26


Esempio completo su DEBUG (2/3)

#ifdef DEBUG
printf("Risultato parziale: %lf\n", output);
#endif

output += 15.3;

printf("Output: %lf\n", output);

return 0;
}

Preprocessore - Unità 10 2013/2014 Pagina 27


Esempio completo su DEBUG (3/3)
>gcc –lm –o esempioDEBUG esempioDEBUG.c
>./esempioDEBUG
Inserire un numero positivo
56
Output: 22.783315

Attivare DEBUG:
>gcc –D DEBUG=1 –lm –o esempioDEBUG esempioDEBUG.c
>./esempioDEBUG
Inserire un numero positivo
56
Risultato parziale: 7.483315
Output: 22.783315

Preprocessore - Unità 10 2013/2014 Pagina 28