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 0x3012h: Prohibición de aritmética de punteros sobre void*

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3012h: Prohibición de aritmética de punteros sobre void*

Enunciado normativo

NO DEBE realizarse aritmética de punteros directamente sobre void *. Para avanzar sobre memoria genérica, el puntero DEBE convertirse primero a char *, unsigned char * o uint8_t *.

¿Por qué existe esta regla?

El problema

La aritmética de punteros en C avanza en unidades del tipo apuntado: int *p; p + 1 avanza sizeof(int) bytes. Ese comportamiento depende de que el tamaño del tipo sea conocido. Para void el tamaño no está definido: sizeof(void) es una extensión de algunos compiladores que vale 1, no una garantía del estándar.

Por eso, en C estándar, void *p; p + 1 es inválido. GCC lo tolera como extensión (avanza un byte) y emite advertencias sólo con banderas específicas. La consecuencia es un código que compila en Linux/GCC pero no en otros compiladores, y cuyo significado depende del entorno.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
PortabilidadNo compila en compiladores que no aceptan la extensión.
Bug silenciosoEl avance se interpreta como bytes o como elementos según el compilador.
LegibilidadNo queda claro cuál es la unidad de avance.
MantenibilidadCada compilador exige banderas distintas para advertirlo.

Fundamento en el estándar y en la cátedra

C11 §6.5.6 exige que el tipo apuntado tenga un tamaño definido para la aritmética de punteros; void no lo tiene. C11 §6.2.5 completa el motivo: void es un tipo incompleto que no puede completarse. La cátedra prohíbe la extensión y exige la conversión a char *, la unidad de memoria por excelencia, como en 0x3011h: Si una función recibe un puntero genérico para operaciones de solo lectura, la firma de la función debe utilizar const void* para los buffers genéricos.

Alcance y excepciones

Aplica a void * y a punteros derivados (const void *). No aplica a char * ni a unsigned char *, que tienen tamaño definido y son la alternativa correcta. Tampoco prohíbe comparar punteros void * con NULL, ni convertirlos con un cast; sólo prohíbe la suma o resta directa.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Avance directo sobre void *

void *siguiente = base + desplazamiento;
leer(siguiente);

Por qué falla: base + desplazamiento es aritmética sobre void *, inválida en C estándar. Bajo la extensión de GCC avanza desplazamiento bytes, pero otro compilador puede rechazar la expresión o interpretarla distinto. El código gana en brevedad lo que pierde en portabilidad.

❌ Contraejemplo 2 — sizeof(void) como unidad de tamaño

size_t bytes = cantidad * sizeof(void);
void *bloque = malloc(bytes);

Por qué falla: sizeof(void) no está definido por el estándar; GCC lo trata como 1. La reserva queda dependiendo de una extensión y la intención (un arreglo de bytes) no se expresa. Si se querían cantidad bytes, debe usarse unsigned char y sizeof(unsigned char), o directamente cantidad.

✅ Ejemplo conforme 1 — Conversión a char * para avanzar

unsigned char *bytes = base;
unsigned char *siguiente = bytes + desplazamiento;
leer(siguiente);

La conversión a unsigned char * fija la unidad de avance en bytes, portables por definición (todo objeto tiene tamaño múltiplo de char). El cast explícito deja visible la decisión de trabajar a nivel de bytes.

✅ Ejemplo conforme 2 — Recorrido de un buffer genérico de sólo lectura

size_t contar_ceros(const void *buffer, size_t n)
{
    const unsigned char *bytes = buffer;
    size_t ceros = 0;
    for (size_t i = 0; i < n; i++)
    {
        if (bytes[i] == 0)
        {
            ceros++;
        }
    }
    return ceros;
}

El parámetro es const void * (conforme a 0x3011h: Si una función recibe un puntero genérico para operaciones de solo lectura, la firma de la función debe utilizar const void*) y el recorrido se hace con indexación sobre const unsigned char *. No hay aritmética sobre void * y el tipo de cada operación es claro.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cSuma o resta directa sobre void *; uso de sizeof(void).
gcc / clanggcc -Wall -Wextra -Wpointer-arith -std=c11 ...pointer of type 'void *' used in arithmetic.
clangclang -Weverything ...arithmetic on a pointer to void is a GNU extension.

Checklist de autocontrol

Reglas relacionadas