Visita Orebla.it su Facebook Segui Orebla.it su Twitter Vedi i video su YouTube di Orebla.it Unisciti alle cerchie di Orebla.it
DOVE TI TROVI: \\ Home Page\c\Guida al C : Direttive per il preprocessore

Guida al C/C++

autore: Orebla
xx/xx/xx

Direttive per il preprocessore: la direttiva #define, #if e #ifdef:

La direttiva #define

La direttiva serve a definire una MACRO, ovvero un simbolo.
La sintassi con cui utilizzare la direttiva la seguente:

#define  nome_macro  valore_macro

convenzionalmente i nomi delle macro vengono scritti con delle lettere MAIUSCOLE. Il preprocessore legge la definizione della MACRO e, ogni volta che ne incontra il nome all'interno del file sorgente SOSTITUISCE al simbolo il corrispondente valore, SENZA verificare la correttezza sintattica dell'espressione risultante.

// esempio di utilizzo di una macro

#include <stdio.h>
#define ULTIMO 30


int main()
{int i = 0;

 for(i = 0; i< ULTIMO;i++)
   printf("il numero e\' %d ed il massimo valore e\' %d",i,ULTIMO);

 return 0; }

// fine esempio

Dopo l'azione del preprocessore, la funzione main() avr questo aspetto :

int main()
{int i = 0;

 for(i = 0; i< 30;i++)
   printf("il numero e\' %d ed il massimo valore e\' %d",i,30);

 return 0; }

Ovvero il preprocessore ha sostituito il simbolo ULTIMO con il corrispondente valore.
Il valore della MACRO pu essere formato da un numero arbitrario di caratteri; se risultasse necessario andare a capo, basta inserire un carattere \ al termine della riga.

Ad esempio, nel caso fosse necessario stampare molte volte il medesimo messaggio, si pu definire una macro di questo tipo:


#include <stdio.h>
#define    AVVISO    printf("\nil programma, fino a qui, funziona in modo corretto");


int main()
{char c;
  c = getchar();
... // istruzioni
AVVISO
... // altre istruzioni
AVVISO
... // istruzioni
}

grazie al preprocessore, ovvero effettuando la sostituzione, questo codice si trasforma in :

int main()
{char c;
  c = getchar();
... // istruzioni
printf("\nil programma, fino a qui, funziona in modo corretto");
... // altre istruzioni
printf("\nil programma, fino a qui, funziona in modo corretto");
... // istruzioni
}

Grazie alla direttiva #define si possono definire anche delle macro che svolgono operazioni un po' pi complesse. Ad esempio

#include <stdio.h>
#define STAMPA(a) printf("\nil valore della variabile e\' pari a %d",a);

int main()
{ int a=6, b=9;

   STAMPA(a)
   STAMPA(b)
   STAMPA(a+b)

 return 0;}


produce il seguente output:

il valore della variabile e' pari a 6
il valore della variabile e' pari a 9
il valore della variabile e' pari a 15

ATTENZIONE: la sostituzione pu nascondere dei problemi.

#include <stdio.h>
#define   MOLTIPLICA(a,b)   a*b


int main()
{int a=5,b=3,c;

 c = MOLTIPLICA(a,b);
   // tutto OK ! diventa c = a * b;
 
 c = MOLTIPLICA( a + b, b );     // ?? diventa c = a + b * b;

 return 0; }

Per ovviare al problema basta inserire delle parentesi ...

#define   MOLTIPLICA(a,b)   (a) * (b)
Direttive per la compilazione condizionale: #if ed #ifdef


Cos come, mediante il costrutto if() era possibile controllare il flusso del programma, grazie alle direttiva #if e #ifdef possibile controllare il flusso della compilazione, ovvero stabilire quali porzioni del codice verranno compilate e quali no. La sintassi con cui utilizzare le due direttive la seguente:

#if   espressione_costante

... codice

#else

... codice

#endif

#ifdef   nome_macro

... codice

#endif


Se l'espressione costante vera, allora viene compilata la prima porzione del codice; altrimenti viene compilata la seconda




Se la macro stata definita, allora viene compilata la porzione di codice compresa tra #ifdef ed #endif; altrimenti tale porzione di codice viene saltata


NOTA: le due macro vanno SEMPRE concluse da #endif


// esempio : programma per convertire euro in sterline o dollari

#include <stdio.h>
#define MONETA STERLINA
#define UK

int main()
{double coeff = 1;
 int numero ;
 #if MONETA==STERLINA
 coeff = 0.6943;
 #else
 coeff = 1.2509;
 #endif

  printf("\n inserisci la valuta in euro");
  numero = getchar();
  printf("\n %d euro corrispondono a %f", numero , numero/coeff);

 #ifdef UK
  printf(" sterline");
 #else
 printf(" dollari");
 #endif
 return 0; }

// fine esempio


Provate a riprodurre questo esempio usando l'opzione -E del compilatore. A seconda del valore della macro MONETA vedrete che l'output del preprocessore cambia, ovvero cambia il codice che di volta in volta viene compilato.
Viceversa, usando il costrutto if() TUTTO il codice viene sempre compilato, ma solo una porzione viene eseguita.

IMPORTANTE: non necessario definire una macro all'interno del codice !!! lo possiamo tranquillamente fare anche dalla riga di comando.



gcc -Wall -o test -DNOMEMACRO[=VALOREMACRO] programma.c


-DNOMEMACRO[=VALOREMACRO]

NOMEMACRO  rappresenta il nome della macro
VALOREMACRO   opzionale e rappresentail valore della macro


Prendiamo come esempio il programma di prima. Modificando il codice in questo modo:

#include <stdio.h>


int main()
{double coeff = 1;
 int numero ;
 #if MONETA==STERLINA
 coeff = 0.6943;
 #else
 coeff = 1.2509;
 #endif

  printf("\n inserisci la valuta in euro");
  numero = getchar();
  printf("\n %d euro corrispondono a %f", numero , numero/coeff);

 #ifdef UK
  printf(" sterline");
 #else
 printf(" dollari");
 #endif

 return 0; }

// fine esempio


e compilando con questo comando

gcc -Wall -o test -DMONETA=STERLINA -DUK programma.c
otteniamo il medesimo risultato ottenuto prima.

Nota: chi usa il compilatore Dev-Cpp, per poter usare la stessa metodologia, deve scegliere l'opzione "Opzioni di compilazione" sotto il men strumenti ed inserire, assieme al -Wall queste nuove istruzioni per il compilatore
Esercizio:

Per mettere in pratica tutto questo, costruiamo un programma in cui la medesima procedura viene implementata sia in modo ricorsivo che in modo iterativo. La scelta di quale procedura utilizzare avver in fase di compilazione e NON in fase di esecuzione, ovvero nel codice eseguibile NON CI SARA' TRACCIA dell'implementazione della funzione che non stata selezionata.

La funzione da implementare quella che ci serve a visualizzare il valore dei bit che compongono la rappresentazione binaria di una variabile di tipo intero.

Il metodo da utilizzare quello visto qualche giorno fa: data una variabile di tipo int, per visualizzare il valore del bit della sua rappresentazione che si trova nella posizione corrispondente alla potenza di 2 di ordine n sufficiente dividere il numero stesso per la medesima potenza di 2 e poi applicare l'operatore modulo %2.

10011000010100101011001010100001        mi interessa il bit corrispondente a 2 elevato a 5

------>    passo 1         10011000010100101011001010100001 / 2^5

------>    passo 2         00000100110000101001010110010101

------>    passo 3         00000100110000101001010110010101 % 2 = 1 ! ecco il valore del bit

Se abbiamo a che fare con interi, dovremmo dividere il numero per potenze di 2 che variano da 0 a 31.

// Implementazione iterativa

#include <stdio.h>
#include <math.h>

void show_bits_i(int);

void show_bits_i(int numero)
{int i=0;
 for(i = 31; i >=0; i--)
   printf("%d",((int) (numero/pow(2,i))) %2 );

 } // non serve l'istruzione return, la funzione di tipo void

// Implementazione ricorsiva (1)

// questa implementazione riprende il metodo presentato alcune lezioni fa per la visualizzazione dei bit di
// una variabile

void
show_bits_r(int);

void show_bits_r(int numero)
{
   if(numero>0)    // sarebbe stato equivalente scrivere if(numero)
     {printf("%d", numero %2 );
       i++;
      show_bits_r(numero/2);}
}

In questo modo, per, stampo solo alcuni bit della sequenza di 32, non tutti !

// Implementazione ricorsiva (2)


void show_bits_r(int);

void show_bits_r(int numero)
{static int i=31;
   if(i>0)    // sarebbe stato equivalente scrivere if(i)
     {printf("%d", (int) (numero/pow(2,i)) %2 );
       i++;
      show_bits_r(numero);}
}

Quale differenza notiamo rispetto all'implementazione ricorsiva presentata ieri ?

/* codice finale - inizio . supponiamo di aver inserito le due implementazioni descritte sopra all'interno del file personale myfun.h */

#include "myfun.h"
#include <stdlib.h>

int main()
{char car1,car2;
  int inizio,i;

 printf("\n inserisci un carattere");
 car1 = mygetchar()
 printf("\n inserisci un altro carattere");
 car2 = mygetchar()

 srand(car1*car2); // inizializzo il generatore di numeri casuali

 for(i=0;i<20;i++)
   {
 #ifdef RICORSIVO

   show_bits_r( rand() ); // implementazione ricorsiva

 #else

   show_bits_i( rand() ); // implementazione iterativa

 #endif

// codice finale - fine

Se l'utente definisce la macro RICORSIVO allora viene compilata SOLO LA PRIMA PARTE mentre se l'utente non la definisce viene automaticamente compilata la seconda.






Articoli utili:
Jailbreak iOS 10: tweaks compatibili

Recensione iPhone 6

IPhone 7 uscito, novita, prezzi