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 0x1013h: Detector de expresiones booleanas complejas sin paréntesis aclaratorios

Estructuras de control y flujo (0x10XX)

Universidad Nacional de Río Negro

0x1013h: Detector de expresiones booleanas complejas sin paréntesis aclaratorios

Enunciado normativo

Cuando en una misma expresión se combinen los operadores lógicos && y ||, DEBEN usarse paréntesis que expliciten la agrupación deseada, aun cuando la precedencia del lenguaje ya la determine. NO DEBE dejarse la agrupación librada a la precedencia implícita.

¿Por qué existe esta regla?

El problema

El lenguaje resuelve la precedencia: && liga más fuerte que ||, por lo que a && b || c significa (a && b) || c. El compilador no tiene dudas, pero el lector sí: la mayoría de las personas no recuerda la tabla de precedencia y debe detenerse a pensarla.

Los paréntesis son documentación ejecutable. Cuentan la agrupación que el autor quiso, no la que el estándar impone. Si coinciden, no cambian la semántica; si no coinciden, revelan un bug de intención antes de que llegue a producción.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Bug de intenciónLa agrupación real no es la que el autor imaginaba.
LegibilidadEl lector debe aplicar la tabla de precedencia para entender.
MantenibilidadAgregar un término cambia la agrupación de forma invisible.
RevisiónEl corrector no puede distinguir intención de accidente.

Fundamento en el estándar y en la cátedra

ISO/IEC 9899:2011 §6.5.13 y §6.5.14 fijan la precedencia de && sobre ||. La cátedra no discute esa precedencia: exige hacerla explícita. Se relaciona con 0x1004h: Las condiciones complejas deben simplificarse o comentarse (extraer subcondiciones nombradas) y con 0x1010h: Prohibición de comparaciones encadenadas no idiomáticas en C (a < b < c) (comparaciones encadenadas). Los paréntesis también evitan que un & o | mal tecleado pase inadvertido.

Alcance y excepciones

Ejemplos exhaustivos

❌ Contraejemplo 1 — mezcla sin paréntesis

if (a && b || c)
{
    actuar();
}

Por qué falla: aunque el compilador agrupa (a && b) || c, el lector debe recordarlo. Si el autor pretendía a && (b || c), el código es incorrecto y no hay ninguna pista.

❌ Contraejemplo 2 — bit a bit en lugar de lógico

if (a > 0 & b > 0)
{
    actuar();
}

Por qué falla: & evalúa ambos lados siempre, sin cortocircuito. Si el segundo operando desreferencia un puntero, puede provocar un fallo que && habría evitado.

✅ Ejemplo conforme 1 — paréntesis explícitos

if ((a && b) || c)
{
    actuar();
}

La agrupación deseada queda escrita; el lector no necesita la tabla de precedencia.

✅ Ejemplo conforme 2 — cortocircuito con operadores lógicos

if (ptr != NULL && (ptr->activo || ptr->es_admin))
{
    actuar();
}

Los paréntesis aclaran que la alternancia interna se combina con la validez del puntero, y && garantiza que ptr->activo no se evalúe si ptr es NULL.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cRegla 0x1013h: mezcla de &&/`
gccgcc -Wall -Wextra -WparenthesesSuele sugerir paréntesis al mezclar operadores lógicos.
Revisión manual&& y `

Checklist de autocontrol

Reglas relacionadas

Antipatrón: Expresión booleana tautológica o contradictoria

Síntoma en el código del estudiante

La condición combina una variable consigo misma de forma que el resultado es siempre verdadero o siempre falso:

if (x && !x)
{
    marcar_error();
}

if (activo || !activo)
{
    registrar();
}

Diagnóstico

Mecanismo del defecto

Por álgebra booleana, x && !x es siempre falso y x || !x es siempre verdadero, para cualquier valor de x. El compilador puede doblar la rama muerta, pero la intención del autor queda sepultada. Estos patrones suelen aparecer cuando se agregan términos sin revisar la condición completa.

Consecuencia observable

La rama inalcanzable (x && !x) nunca se ejecuta y el autor cree que sí; la rama tautológica (x || !x) se ejecuta siempre. En cualquiera de los dos casos el programa “hace algo” y no hay error, lo que convierte al defecto en un bug silencioso de lógica.

Fundamento en el estándar C11

ISO/IEC 9899:2011 §6.8.4.1 define la evaluación de condiciones y §6.5.13–6.5.14 la semántica de && y ||. El estándar no considera estas expresiones inválidas; son lógicamente degeneradas. La cátedra las trata como señal de error de razonamiento y pide simplificarlas o eliminarlas.

Corrección idiomática

❌ Código con el antipatrón
if (x && !x)
{
    marcar_error();
}

if (activo || !activo)
{
    registrar();
}
✅ Código refactorizado
/* la primera rama es inalcanzable: se elimina */

if (activo)
{
    registrar();
}

Si la intención era detectar algo imposible, la condición debe reescribirse. Si era una tautología, sobra la condición y queda la sentencia directa.

Errores típicos al compilar o ejecutar

Con -Wall y optimización:
warning: logical 'and' of mutually exclusive expressions is always false
warning: logical 'or' of collectively exhaustive expressions is always true

La rama marcar_error() nunca se ejecuta; la rama registrar() se ejecuta
siempre, con independencia de activo.

Checklist de verificación

Reglas relacionadas

Antipatrón: Uso de operador bit a bit (&, |) en condición lógica en lugar de booleano (&&, ||)

Síntoma en el código del estudiante

La condición usa & o | (bit a bit) donde se pretendía la lógica booleana && o ||:

if (a > 0 & b > 0)
{
    actuar();
}

Diagnóstico

Mecanismo del defecto

& y | operan bit a bit y evalúan siempre ambos operandos; && y || evalúan el segundo solo si hace falta (cortocircuito, ISO/IEC 9899:2011 §6.5.13–6.5.14). Sobre resultados de comparación (0 o 1) el resultado aritmético coincide muchas veces, lo que esconde el problema. La diferencia se manifiesta cuando el operando derecho tiene efectos secundarios o es inválido.

Consecuencia observable

Con &, una expresión como ptr != NULL & ptr->campo > 0 evalúa ptr->campo aunque ptr sea NULL, provocando un fallo de segmentación que && habría evitado. Además, si los lados no son 0/1 sino máscaras, el resultado difiere por completo del esperado.

Fundamento en el estándar C11

ISO/IEC 9899:2011 §6.5.10–6.5.12 definen &, ^ y | como operadores bit a bit, y §6.5.13–6.5.14 los lógicos con cortocircuito. Usar los primeros como condición booleana no es un error de sintaxis, pero viola la intención y la seguridad del cortocircuito. La cátedra exige los operadores lógicos.

Corrección idiomática

❌ Código con el antipatrón
if (a > 0 & b > 0)
{
    actuar();
}

if (ptr != NULL & ptr->activo)
{
    registrar(ptr);
}
✅ Código refactorizado
if (a > 0 && b > 0)
{
    actuar();
}

if (ptr != NULL && ptr->activo)
{
    registrar(ptr);
}

Con && el segundo operando solo se evalúa si el primero es verdadero: la desreferencia de ptr queda protegida por la comprobación de NULL.

Errores típicos al compilar o ejecutar

Con -Wall:
warning: suggest parentheses around comparison in operand of '&'
[-Wparentheses]

En ejecución, la versión con '&' y ptr == NULL:
Segmentation fault (core dumped)
- o con -fsanitize=address:
ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000

Checklist de verificación

Reglas relacionadas