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 0x3003h: No mezcles operaciones de asignación y comparación en una sola línea

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3003h: No mezcles operaciones de asignación y comparación en una sola línea

Enunciado normativo

NO DEBE combinarse una asignación con = y una comparación en la misma sentencia. La asignación DEBE ejecutarse en una línea propia y la comparación del resultado, en la línea siguiente.

¿Por qué existe esta regla?

El problema

La construcción if ((ptr = malloc(n)) == NULL) concentra dos efectos —guardar la dirección y preguntar si es nula— en una sola expresión. El operador = es de asignación y == de comparación; a simple vista se diferencian por un único carácter. Ese parecido es la causa de la confusión clásica: un = de más compila (con advertencia) y cambia por completo la semántica.

Además, la asignación dentro de una condición tiene un efecto colateral en medio de la evaluación: el lector debe recordar que ptr quedó modificado recién cuando la condición se completa. Separar las operaciones hace explícito el orden de los efectos y elimina la ambigüedad.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Bug lógicoConfundir = con == produce una condición siempre verdadera (valor asignado distinto de cero).
LegibilidadEl lector debe resolver precedencia y efecto colateral de memoria.
DepuraciónEl depurador muestra la línea entera; no se puede inspeccionar el valor asignado antes de ramificar.
MantenibilidadLa mezcla impide agregar validaciones intermedias entre la reserva y su uso.

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

C11 §6.5.16 regula la asignación como operador con efecto colateral y valor resultante; la construcción es válida, pero la cátedra prioriza la claridad (0x0001h: La claridad y prolijidad son de máxima importancia) sobre la compactación. La regla es coherente con 0x1009h: Prohibición de asignaciones simples dentro de condiciones lógicas (prohibición de asignaciones dentro de condiciones lógicas) y con 0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control (prohibición de asignaciones embebidas en el control), y las refuerza puntualmente para el caso de los punteros.

Alcance y excepciones

Aplica a toda combinación de = con ==, !=, <, > dentro de if, while, for o expresiones de retorno. No prohíbe el uso de = como inicializador en la declaración (int x = f();), porque allí no hay comparación. La excepción que menciona 0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control para bucles de lectura tampoco se admite en esta cátedra: siempre se separa la lectura de la prueba.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Reserva dentro del if

if ((ptr = malloc(sizeof(*ptr))) == NULL)
{
    return -1;
}

Por qué falla: si en una reescritura alguien borra un paréntesis o un =, el significado cambia sin error de compilación evidente. Además, no se puede hacer un assert(ptr != NULL) ni registrar el valor entre la reserva y la rama.

❌ Contraejemplo 2 — Lectura y comparación en el while

while ((caracter = *cursor) != '\0')
{
    procesar(caracter);
    cursor++;
}

Por qué falla: mezcla el avance del cursor, la lectura y la prueba de fin en una sola línea. Si la condición se modifica, el efecto de escritura sobre caracter queda oculto dentro de la expresión y es fácil olvidar que ocurre en cada iteración.

✅ Ejemplo conforme 1 — Asignación y comparación separadas

ptr = malloc(sizeof(*ptr));
if (ptr == NULL)
{
    return -1;
}

Cada línea tiene un único efecto. La revisión es directa: la primera reserva, la segunda decide. La regla se combina armónicamente con la verificación de 0x3001h: Siempre verificá la asignación exitosa de memoria dinámica.

✅ Ejemplo conforme 2 — Lectura separada en el lazo

caracter = *cursor;
while (caracter != '\0')
{
    procesar(caracter);
    cursor++;
    caracter = *cursor;
}

El lazo conserva una condición simple de cota o de estado, y el avance se lee en el cuerpo. Cuando la lógica se vuelve más compleja, la separación permite migrar a un lazo con bandera sin reescribir la condición.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.c= dentro de la condición de if/while/for.
gcc / clanggcc -Wall -Wextra -std=c11 ...suggest parentheses around assignment used as truth value.
gcc / clanggcc -Wall -Wextra -Wparentheses ...Advertencia explícita sobre la asignación en contexto booleano.

Checklist de autocontrol

Reglas relacionadas