Regla 0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control
Estructuras de control y flujo (0x10XX)
0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control¶
Enunciado normativo¶
NO DEBEN embeberse expresiones de asignación dentro de la condición de una estructura de control, ni siquiera comparadas con otro valor (
if ((x = f()) != 0),while ((c = getchar()) != EOF)). La asignación DEBE escribirse en una sentencia propia, antes o dentro del cuerpo.
¿Por qué existe esta regla?¶
El problema¶
if ((x = f()) != 0) hace tres cosas en una línea: llama a f, guarda el
resultado en x y decide. El lector debe separar mentalmente esas acciones y,
además, el patrón se parece demasiado a un == mal escrito, lo que fomenta
confusión entre asignar y comparar.
La variante con while ((c = getchar()) != EOF) es idiomática en C histórico,
pero enseña mal: acostumbra a meter efectos secundarios en las condiciones. La
cátedra prefiere la asignación explícita, que separa “obtener” de “decidir”.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Legibilidad | Efectos secundarios escondidos en la condición del control. |
| Bug lógico | Confusión con == y asignaciones que se ejecutan sin querer. |
| Depuración | No se puede observar el valor intermedio sin reestructurar. |
| Duplicación | El código de lectura se repite antes del lazo y dentro del cuerpo. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §6.5.16 define la asignación como expresión y permite este uso; la restricción es pedagógica. Se complementa con 0x1009h: Prohibición de asignaciones simples dentro de condiciones lógicas, que prohíbe la asignación simple como condición, y con 0x3003h: No mezcles operaciones de asignación y comparación en una sola línea, que separa asignación y comparación. La cátedra prioriza una acción por sentencia.
Alcance y excepciones¶
Prohibido asignar dentro de
if,while,for,do-whiley en argumentos de control.La inicialización del
for(int i = 0;) no es una condición y no está alcanzada.No hay excepción por “es más compacto”: la lectura previa siempre es posible.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — lectura con asignación embebida¶
int c;
while ((c = getchar()) != EOF)
{
procesar(c);
}Por qué falla: la lectura y la decisión comparten sentencia. Si procesar
cambia algo en la lógica de lectura, hay que reestructurar todo el patrón.
❌ Contraejemplo 2 — resultado de función asignado en el if¶
if ((resultado = procesar(dato)) == 0)
{
reportar_exito();
}Por qué falla: resultado se modifica dentro de la condición; un lector que
busca dónde se asigna debe mirar dentro del if. Si además el autor escribe
= en lugar de ==, el error es difícil de ver.
✅ Ejemplo conforme 1 — lectura separada¶
int c = getchar();
while (c != EOF)
{
procesar(c);
c = getchar();
}La obtención del dato es una sentencia visible; la condición solo compara. Se lee de arriba hacia abajo sin efectos ocultos.
✅ Ejemplo conforme 2 — resultado en sentencia propia¶
int resultado = procesar(dato);
if (resultado == 0)
{
reportar_exito();
}La asignación y la decisión quedan separadas; cada línea hace una sola cosa y el valor intermedio es observable.
⚠️ Casos límite¶
strlenen la condición delfor: repetirstrlen(s)cada iteración es O(n²); guardar la longitud antes del lazo (ver 0x301Bh: Prohibición de casts de tipo innecesarios o redundantes).scanfdentro dewhile: preferir la lectura explícita y verificar su valor de retorno.forcon dos asignaciones: el encabezado acepta varias, pero si hay efectos secundarios conviene unwhile(ver 0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for).
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Regla 0x1012h: asignación en control. |
gcc | gcc -Wall -Wextra -Wparentheses | warning: suggest parentheses around assignment. |
| Revisión manual | — | = o (x = f()) dentro de una condición. |
Checklist de autocontrol¶
¿Alguna condición contiene una asignación embebida?
¿Saqué la asignación a una sentencia previa o al cuerpo?
¿Evité recalcular
strlenu otro costo en cada iteración?¿La lectura de
scanf/getchares explícita y verificada?
Reglas relacionadas¶
0x1009h: Prohibición de asignaciones simples dentro de condiciones lógicas — prohíbe la asignación simple usada como condición.
0x3003h: No mezcles operaciones de asignación y comparación en una sola línea — no mezclar asignación y comparación en una línea.
0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for — el
forcon corte compuesto corresponde a unwhile.
Antipatrón: Precedencia errónea entre asignación y comparación¶
Síntoma en el código del estudiante¶
En una misma expresión aparecen = y == sin paréntesis que separen la
asignación de la comparación:
if (p = malloc(10) == NULL)
{
reportar_error();
}Diagnóstico¶
Mecanismo del defecto¶
La precedencia de == es mayor que la de = (ISO/IEC 9899:2011 §6.5.16 y
§6.5.9). Por lo tanto la expresión se agrupa como p = (malloc(10) == NULL):
primero se evalúa la comparación del puntero contra NULL (resultado booleano)
y ese 0 o 1 se asigna a p. El puntero devuelto por malloc nunca queda
guardado.
Consecuencia observable¶
p recibe 0 o 1 en lugar de una dirección válida; usarlo después produce un
acceso a memoria inválido o un free incorrecto. Si malloc falló, la
comparación es verdadera y p queda en 1, un puntero no nulo e inutilizable.
Es comportamiento indefinido al desreferenciar.
Fundamento en el estándar C11¶
ISO/IEC 9899:2011 §6.5.16 (asignación simple) y §6.5.9 (==) fijan la
precedencia que causa el agrupamiento. La cátedra prohíbe las asignaciones
embebidas en condiciones (0x1012h) y exige separar la operación. El resultado
correcto requiere paréntesis: (p = malloc(10)) == NULL.
Corrección idiomática¶
❌ Código con el antipatrón¶
if (p = malloc(10) == NULL)
{
reportar_error();
}✅ Código refactorizado¶
p = malloc(10);
if (p == NULL)
{
reportar_error();
}La asignación se separa de la comparación; no hay ambigüedad de precedencia y el
valor de p es el que devolvió malloc. Si se necesitara mantener el patrón en
una línea, el paréntesis obligatorio sería if ((p = malloc(10)) == NULL).
Errores típicos al compilar o ejecutar¶
Con -Wall -Wparentheses:
warning: suggest parentheses around comparison in operand of '='
[-Wparentheses]
En ejecución: p vale 0 o 1; al desreferenciarlo o al liberarlo,
segmentation fault o corrupción del heap.
Con -fsanitize=address:
ERROR: AddressSanitizer: SEGV on unknown address 0x000000000001Checklist de verificación¶
¿Alguna condición combina
=y==sin paréntesis?¿La asignación quedó guardada en la variable correcta?
¿Separé la operación de la comparación?
¿Verifiqué que
mallocno devolvióNULLantes de usarp?
Reglas relacionadas¶
0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control — prohíbe asignaciones embebidas en condiciones.
0x3001h: Siempre verificá la asignación exitosa de memoria dinámica — verificar siempre el retorno de la memoria dinámica.
0x100Eh: Delimitación obligatoria con bloque de llaves en lazos do-while — regla asociada a este antipatrón.