Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regla 0x5013h: Protección obligatoria con paréntesis envolventes en expresiones de macroconstantes (#define)

Compilacion, preprocesador y seguridad (0x50XX)

Universidad Nacional de Río Negro

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 consecuenciaEfecto concreto
Resultado incorrectoTAM * 2 con #define TAM 10 + 5 da 20, no 30.
Bug silenciosoLa macro «funciona» en usos simples y falla en expresiones.
MantenibilidadCada 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

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cReporta 0x5013h y gaff fix agrega los paréntesis.
gccgcc -E archivo.cSe ve la expresión expandida sin agrupar en el punto de uso.

Checklist de autocontrol

Reglas relacionadas