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 0x2018h: Los comparadores no deben usar resta sujeta a desbordamiento

Funciones, contratos y modularizacion (0x20XX)

Universidad Nacional de Río Negro

0x2018h: Los comparadores no deben usar resta sujeta a desbordamiento

Enunciado normativo

Los comparadores NO DEBEN restar sus argumentos; DEBEN devolver -1, 0 o 1 mediante comparaciones sin desbordamiento.

Síntoma en el código del estudiante

El comparador clásico de qsort escrito como una sola resta: return *(int *)a - *(int *)b;. La función entra en pocas líneas, compila sin advertencias y ordena bien la mayoría de las listas. El problema aparece solo con valores extremos.

Diagnóstico

Mecanismo del defecto

La resta entre dos int puede desbordar el rango representable. Si a es INT_MAX y b es negativo, a - b supera INT_MAX y el resultado no es representable. En C, el desborde de enteros con signo es comportamiento indefinido: el procesador puede dar la vuelta (wraparound), saturar o producir un valor cualquiera. Cuando da la vuelta, el signo del resultado puede invertirse y el orden de esos dos elementos queda mal.

El error es silencioso en la mayoría de las pruebas porque los valores chicos no se acercan a los bordes. Además, el patrón concentra varias operaciones —desreferenciar, castear, restar— en una sola expresión, lo que lo emparenta con la compactación que la regla 0x2011h: Prohibición de asignaciones múltiples a una variable sin lectura intermedia (dead store) desaconseja.

Consecuencia observable

Un arreglo con INT_MAX e INT_MIN puede ordenarse de forma incorrecta o el programa abortar con SIGFPE/SIGABRT según la plataforma. Con -fsanitize=undefined aparece runtime error: signed integer overflow.

Fundamento en el estándar C11

C11 §6.5 párrafo 5 establece que si una operación produce un resultado no representable en el tipo, el comportamiento es indefinido. El desborde con signo no es modular (a diferencia del no signado). qsort está declarada en <stdlib.h> (§7.22.5) y exige que el comparador devuelva un entero negativo, cero o positivo, no la diferencia exacta.

Corrección idiomática

❌ Código con el antipatrón

int cmp(const void *a, const void *b)
{
    return *(int *)a - *(int *)b;
}

✅ Código refactorizado

int cmp(const void *a, const void *b)
{
    int va = *(const int *)a;
    int vb = *(const int *)b;

    if (va < vb) {
        return -1;
    }
    if (va > vb) {
        return 1;
    }

    return 0;
}

Las comparaciones nunca desbordan y el resultado tiene el signo correcto en todo el dominio. Además los punteros se reciben como const (0x3007h: Los argumentos de tipo puntero deben ser const siempre que la función no los modifique) y no se mezclan varias operaciones en una sola expresión.

Errores típicos al compilar o ejecutar

$ gcc -fsanitize=undefined comparador.c && ./a.out
comparador.c:3:22: runtime error: signed integer overflow:
  2147483647 - (-2147483648) cannot be represented in type 'int'

Checklist de verificación

Reglas relacionadas