Pré processador e constantes

Um amigo me mostrou um problema interessante que, confesso, levei algum tempo para resolver. O fragmento de código abaixo simplesmente não compila, causando um erro de “invalid suffix ‘…’ on integer or constant“:

/* fragmento de keys.h */
...
#define OFFSET 0x1000
...
#define ALT_1  (0xf8+OFFSET)
#define ALT_2  (0xf9+OFFSET)
#define ALT_3  (0xfa+OFFSET)
#define ALT_4  (0xfb+OFFSET)
#define ALT_5  (0xfc+OFFSET)
#define ALT_6  (0xfd+OFFSET)
#define ALT_7  (0xfe+OFFSET)
#define ALT_8  (0xff+OFFSET)
#define ALT_9  (0x80+OFFSET)
#define ALT_0  (0x81+OFFSET)
...
/* fragmento de main.c */
...
int altconvert[] = {
  ALT_A,ALT_B,ALT_C,ALT_D,ALT_E,ALT_F,ALT_G,ALT_H,
  ALT_I,ALT_J,ALT_K,ALT_L,ALT_M,ALT_N,ALT_O,ALT_P,
  ALT_Q,ALT_R,ALT_S,ALT_T,ALT_U,ALT_V,ALT_W,ALT_X,
  ALT_Y,ALT_Z,ALT_0,ALT_1,ALT_2,ALT_3,ALT_4,ALT_5,
  ALT_6,ALT_7,ALT_8,ALT_9
};

O erro aparece na linha que define o símbolo ALT_7 para o pré processador:

keys.h:97:21: error: invalid suffix "+OFFSET" on integer constant
 #define ALT_7 (0xfe+OFFSET)
                ^
main.c:9:11: note: in expansion of macro ‘ALT_7’
  ALT_6,ALT_7,ALT_8,ALT_9
        ^

Meu amigo ficou se perguntando isso não seria um bug do compilador… E a pergunta que fiquei me fazendo é: Por que diabos as definições de ALT_1 até ALT-6 e de ALT_8 e ALT_9 não deram o mesmo problema?

A diferença entre ALT_6, ALT_7 e ALT_8 é apenas uma: ALT_7 é um valor em hexadecimal terminada em ‘e’ e as demais não… Qual é mesmo a maneira de declarar constantes que tenham um ‘e’? Ahhhhh… ponto flutuante:

float c = 1e+10;

Acontece que os valores associados a ponto flutuante têm que ser expressos em decimal. Não podem ser hexadecimais… A não ser que estejamos falando do padrão C99 e, neste caso, o caractere ‘e’ teria que ser trocado por ‘p’:

float c = 0xfp+10;

Note que o expoente continua tendo que ser decimal…

No caso apresentado, o símbolo ALT_7 é definido como a string ‘0xfe+0x1000’. O ‘e’, seguido do valor inteiro, indica uma constante em ponto flutuante, mas hexadecimal… dai o erro. Que faz parte da especificação da linguagem! Não se trata de um bug!

Existem duas soluções possíveis:

  1. Colocar os valores inteiros na definição dos símbolos entre colchetes;
  2. Colocar um espaço entre os valores e o operador + (ou -).

É bom lembrar que o pré processador faz apenas substituições léxicas. Quando definimos um macro, como em:

#define advance_ptr(p) { (p)++; }

O pré compilador não efetua a operação… em qualquer parte do código onde advance_ptr for encontrado ele apenas substituirá p pelo parâmetro passado no macro… É o compilador que avaliará a operação ++, mas apenas depois que o pré compilador terminar suas substituições!

Portanto, a dica aqui é simples: Não assuma que o pré compilador “compila” alguma coisa ou avalia expressões… Nope, ele nunca fará isso!… Apenas “substituirá”. E “compilador” só tem um… a ênfase no nome deve ser no “pré”…

Anúncios