Regla 0x3019h: Prohibición de comparar punteros contra constantes numéricas distintas de NULL o cero
Memoria, punteros y tipos (0x30XX)
0x3019h: Prohibición de comparar punteros contra constantes numéricas distintas de NULL o cero¶
Enunciado normativo¶
NO DEBE compararse un puntero contra literales numéricos mayores que cero (
ptr == 1,ptr > 0,ptr == 0x1000). La única comparación numérica permitida es contra la constante de puntero nulo:NULL(o0como constante nula, según 0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0).
¿Por qué existe esta regla?¶
El problema¶
Un puntero es una dirección de memoria, no un número con el que se razona aritméticamente. En C, comparar un puntero contra un entero distinto de cero es una restricción que el compilador debe diagnosticar; si se fuerza con un cast, el resultado es definido por la implementación y no portable.
El problema pedagógico es más profundo: if (ptr > 0) sugiere que las
direcciones “positivas” indican validez y que existe un orden entre punteros. No
es así. Las únicas relaciones definidas entre punteros son igualdad y, dentro del
mismo objeto arreglo, orden relativo (C11 §6.5.8). Fuera de ese contexto, > es
comportamiento indefinido.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| No portabilidad | La comparación depende de la representación de direcciones. |
| Comportamiento indefinido | Comparar con > punteros de objetos distintos (C11 §6.5.8). |
| Confusión de concepto | Se trata la dirección como un número mágico en lugar de un recurso. |
| Mantenibilidad | El significado del literal no está definido en ninguna parte. |
Fundamento en el estándar y en la cátedra¶
C11 §6.3.2.3 define que un entero constante con valor 0, o tal constante
convertida a void *, es una constante de puntero nulo. Cualquier otro
entero no es una constante de puntero nulo válida. C11 §6.5.8 sólo define las
comparaciones relacionales entre punteros al mismo objeto arreglo. La cátedra
exige que todo puntero se compare contra NULL, en coherencia con
0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0.
Alcance y excepciones¶
Aplica a todos los tipos de puntero, incluidos punteros a función.
No prohíbe comparar punteros entre sí dentro del mismo arreglo para delimitar
subrangos, ni comparar contra NULL. Tampoco regula los valores enteros que se
guardan en un uintptr_t para diagnóstico: allí la conversión es explícita y su
uso no es una comparación de punteros.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Comparación contra 1¶
if (ptr == 1)
{
printf("Valido\n");
}Por qué falla: 1 no es una constante de puntero nulo; es un entero. La
comparación es una restricción y, si se fuerza con un cast a
(int *)1, el compilador debe producir una dirección con ese valor, práctica no
portable. El código sugiere que la dirección 1 tiene un significado especial,
pero ese significado no está definido.
❌ Contraejemplo 2 — Ordenamiento de punteros no relacionados¶
if (ptr > 0)
{
procesar(ptr);
}Por qué falla: ptr > 0 compara el puntero contra el entero cero; además, la
relación > entre un puntero y un entero no está definida por el estándar. La
intención real —“el puntero es válido”— se expresa con ptr != NULL, que es
portable y semánticamente correcto.
✅ Ejemplo conforme 1 — Comparación contra NULL¶
if (ptr != NULL)
{
procesar(ptr);
}NULL es la constante de puntero nulo: la comparación es válida y expresa la
idea de validez. Es la forma que exige 0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0 y la que el compilador
entiende sin conversiones.
✅ Ejemplo conforme 2 — Orden dentro del mismo arreglo¶
int *inicio = valores;
int *fin = valores + cantidad;
for (int *p = inicio; p < fin; p++)
{
procesar(*p);
}La comparación < es válida porque inicio y fin apuntan al mismo objeto
arreglo. El orden relativo está definido por C11 §6.5.8 y el recorrido es
portable. No hay literales mágicos ni comparaciones contra enteros.
⚠️ Casos límite¶
Direcciones de hardware: en sistemas embebidos se usan direcciones fijas como
(volatile uint32_t *)0x40000000; no es el dominio de esta cátedra y su uso exige justificación.uintptr_t: convertir un puntero a entero para hashing o registro es válido si el tipo es suficientemente ancho; la comparación se hace ya entre enteros.Comparar dos punteros nulos:
p1 == p2es válido aunque ambos seanNULL.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Puntero comparado contra literal distinto de NULL/0. |
gcc / clang | gcc -Wall -Wextra -std=c11 ... | comparison between pointer and integer o ordered comparison of pointer with integer. |
cppcheck | cppcheck --enable=all archivo.c | comparePointers con literal. |
Checklist de autocontrol¶
¿Toda comparación de puntero es contra
NULL?¿Evité literales como
1o0x1000para “puntero válido”?¿Las comparaciones
</>ocurren dentro del mismo arreglo?¿Usé
!= NULLen lugar de> 0para preguntar por validez?¿Si necesito un entero, convierto con
uintptr_tde forma explícita?
Reglas relacionadas¶
0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0 — los punteros se comparan con
NULL, no con0.0x1005h: Reemplazá las condiciones ambiguas basadas en la ‘veracidad’ (truthiness) del tipo de dato — comparaciones explícitas y acordes al tipo.
0x3012h: Prohibición de aritmética de punteros sobre void* — tampoco se hace aritmética directa sobre
void *.0x3002h: Liberá siempre la memoria dinámica y asigná NULL al puntero para mitigar punteros colgantes — la validez de un puntero se restablece anulándolo con
NULL.