Regla 0x5013h: Protección obligatoria con paréntesis envolventes en expresiones de macroconstantes (#define)
Compilacion, preprocesador y seguridad (0x50XX)
0x5013h: Protección obligatoria con paréntesis envolventes en expresiones de macroconstantes (#define)¶
Enunciado normativo¶
DEBE encerrarse entre paréntesis toda expresión aritmética, lógica o condicional usada como valor de una macroconstante
#define, y también la expresión completa.
¿Por qué existe esta regla?¶
El problema¶
Una macroconstante es una sustitución textual sin tipos. Si TAM se define como
10 + 5, entonces TAM * 2 se expande a 10 + 5 * 2, que por precedencia vale
20 y no 30. El autor escribió una expresión y el uso la rompe: el error no
está en la definición sino en cada punto de uso, donde es mucho más difícil de
ver.
El problema es simétrico al de las macros funcionales (0x500Ah: Protección obligatoria de parámetros en macros funcionales mediante paréntesis), pero sin
parámetros que protejan: acá sólo hay paréntesis alrededor de la expresión
completa (y de sus subexpresiones cuando corresponde). El autofix de gaff
agrega los paréntesis faltantes, porque el patrón es puramente sintáctico.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Resultado incorrecto | TAM * 2 con #define TAM 10 + 5 da 20, no 30. |
| Bug silencioso | La macro «funciona» en usos simples y falla en expresiones. |
| Mantenibilidad | Cada uso exige recordar la precedencia interna de la macro. |
| Operadores lógicos | #define MASCARA 0x0F | 0xF0 cambia su significado al combinarse. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §6.10.3 describe la sustitución de macros: el cuerpo se
reemplaza por la secuencia de tokens tal cual, sin agrupar. §6.5 establece las
precedencias que decidirán el resultado. La cátedra exige el doble paréntesis y
recomienda, cuando el valor lo permite, un enum o un const, que sí tienen
tipo y alcance.
Alcance y excepciones¶
Aplica a toda macroconstante cuyo cuerpo sea una expresión. Excepciones: un
literal único (#define MAX 100) no necesita paréntesis; una cadena o un
carácter tampoco. Si el valor es un entero, conviene evaluarlo y usar
enum { TAM = 15 };, que es una constante de compilación con tipo.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Expresión aritmética sin proteger¶
#define TAM 10 + 5
int main(void)
{
int doble = TAM * 2;
return doble;
}Por qué falla: TAM * 2 se expande a 10 + 5 * 2, que vale 20 en lugar de
30. La definición parece correcta hasta que se usa dentro de otra operación.
❌ Contraejemplo 2 — Expresión lógica y de corrimiento¶
#define MASCARA 0x0F | 0xF0
int main(void)
{
int x = MASCARA & 0x0F;
return x;
}Por qué falla: MASCARA & 0x0F se expande a 0x0F | 0xF0 & 0x0F; como &
tiene más precedencia que |, da 0x0F | 0x00 = 0x0F en vez de 0x00. El
paréntesis del cuerpo lo habría evitado.
✅ Ejemplo conforme 1 — Paréntesis envolventes¶
#define TAM ((10) + (5))
#define MASCARA (0x0F | 0xF0)
int main(void)
{
int doble = TAM * 2;
int x = MASCARA & 0x0F;
return doble + x;
}Con la expresión completa entre paréntesis, TAM * 2 da 30 y MASCARA & 0x0F da 0x00, sin importar el contexto donde se usen.
✅ Ejemplo conforme 2 — enum en lugar de macro¶
enum {
TAM = 15,
MASCARA = 0x0F | 0xF0
};
int main(void)
{
return TAM * 2;
}El enum provee constantes con nombre, tipo entero y alcance de bloque; no hay
sustitución textual y por lo tanto no hay sorpresas de precedencia. Es la
alternativa preferida cuando el valor es un entero.
⚠️ Casos límite¶
Literal único:
#define MAX 100no requiere paréntesis, aunque agregarlos no molesta.Cadenas y caracteres:
#define SALUDO "hola"es un token único; los paréntesis no aplican.#define NEG -1:-(NEG)sería- - 1; conviene#define NEG (-1).Compatibilidad con
enum: si la macro se usa como tamaño de arreglo, unenumtambién sirve como constante de compilación (0x5001h: Los arreglos estáticos deben ser creados con un tamaño fijo en tiempo de compilación).
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Reporta 0x5013h y gaff fix agrega los paréntesis. |
gcc | gcc -E archivo.c | Se ve la expresión expandida sin agrupar en el punto de uso. |
Checklist de autocontrol¶
¿Toda macroconstante con expresión está envuelta en
( ... )?¿Las subexpresiones con operadores de distinta precedencia también lo están?
¿El valor entero podría expresarse mejor con
enum?¿Corrí
gaff fixpara proteger las expresiones automáticamente?
Reglas relacionadas¶
0x500Ah: Protección obligatoria de parámetros en macros funcionales mediante paréntesis — la protección de parámetros en macros funcionales.
0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE — nomenclatura de constantes en mayúsculas.