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 0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control

Estructuras de control y flujo (0x10XX)

Universidad Nacional de Río Negro

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 consecuenciaEfecto concreto
LegibilidadEfectos secundarios escondidos en la condición del control.
Bug lógicoConfusión con == y asignaciones que se ejecutan sin querer.
DepuraciónNo se puede observar el valor intermedio sin reestructurar.
DuplicaciónEl 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

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

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cRegla 0x1012h: asignación en control.
gccgcc -Wall -Wextra -Wparentheseswarning: suggest parentheses around assignment.
Revisión manual= o (x = f()) dentro de una condición.

Checklist de autocontrol

Reglas relacionadas

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 0x000000000001

Checklist de verificación

Reglas relacionadas