Regla 0x3003h: No mezcles operaciones de asignación y comparación en una sola línea
Memoria, punteros y tipos (0x30XX)
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 consecuencia | Efecto concreto |
|---|---|
| Bug lógico | Confundir = con == produce una condición siempre verdadera (valor asignado distinto de cero). |
| Legibilidad | El lector debe resolver precedencia y efecto colateral de memoria. |
| Depuración | El depurador muestra la línea entera; no se puede inspeccionar el valor asignado antes de ramificar. |
| Mantenibilidad | La 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¶
Asignación en la inicialización del
for:for (i = 0; ...)no contiene comparación de esa asignación; está permitida.Cadena de asignaciones:
a = b = c;no mezcla comparación, pero conviene partirla si oculta efectos; la regla no la prohíbe.Macros de terceros: si una macro expande una asignación dentro de una condición, documentalo; la regla apunta al código propio.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | = dentro de la condición de if/while/for. |
gcc / clang | gcc -Wall -Wextra -std=c11 ... | suggest parentheses around assignment used as truth value. |
gcc / clang | gcc -Wall -Wextra -Wparentheses ... | Advertencia explícita sobre la asignación en contexto booleano. |
Checklist de autocontrol¶
¿Separé la asignación de la comparación en dos sentencias?
¿La condición del
ifowhilelee una variable ya asignada?¿Puedo inspeccionar el valor asignado en el depurador antes de ramificar?
¿Evité el
=accidental donde quería==?
Reglas relacionadas¶
0x1009h: Prohibición de asignaciones simples dentro de condiciones lógicas — prohibición general de asignaciones dentro de condiciones.
0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control — prohibición de asignaciones embebidas en estructuras de control.
0x3001h: Siempre verificá la asignación exitosa de memoria dinámica — la reserva separada permite verificar el retorno con claridad.
0x0001h: La claridad y prolijidad son de máxima importancia — la claridad es el criterio rector de esta separación.