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 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*

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

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*

Enunciado normativo

Si una función recibe un puntero genérico void * y NO modifica la memoria apuntada, el parámetro DEBE declararse como const void *. Se prohíbe pasar void * sin calificador cuando la operación es de sólo lectura.

¿Por qué existe esta regla?

El problema

void * es el puntero genérico: puede apuntar a cualquier tipo de objeto, pero no se puede desreferenciar sin convertirlo. Es la herramienta natural para funciones que operan sobre bytes o sobre datos de tipo desconocido, como memcpy, memset, fwrite o un comparador de qsort.

Cuando esa función sólo lee, dejar el parámetro como void * niega el contrato. La versión general de 0x3007h: Los argumentos de tipo puntero deben ser const siempre que la función no los modifique pide const en todo puntero de entrada; const void * es la aplicación de esa regla al puntero genérico. Sin el calificador, no se puede pasar un objeto declarado const, y el compilador no puede avisar si la función escribe donde no debería.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Contrato débilEl llamador no sabe si sus datos serán alterados.
Restricción inversaNo se pueden pasar objetos const ni literales.
Bug silenciosoUna escritura accidental sobre datos de sólo lectura no se detecta.
ReutilizaciónLa función no sirve para datos inmutables.

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

C11 §6.7.3 define const; aplicado a void, protege el objeto apuntado sin comprometer la genericidad. La biblioteca estándar es el modelo: memcmp y memcpy usan const void * para el origen. La cátedra alinea la firma con ese precedente, como especialización de 0x3007h: Los argumentos de tipo puntero deben ser const siempre que la función no los modifique.

Alcance y excepciones

Aplica a funciones genéricas de lectura: impresión, comparación, copia, cálculo de hash, búsqueda. No aplica cuando la función escribe en el buffer (allí el parámetro es void * sin const) ni cuando hay dos parámetros y sólo uno es de lectura. Tampoco obliga a convertir datos concretos a void *: si el tipo es conocido, un const T * es más informativo.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Impresión genérica sin const

void imprimir_hex(void *buffer, size_t n)
{
    unsigned char *bytes = buffer;
    for (size_t i = 0; i < n; i++)
    {
        printf("%02x ", bytes[i]);
    }
}

Por qué falla: la función sólo lee, pero el parámetro void * permite pasar cualquier objeto y sugiere que podría modificarlo. Un llamador con datos const no podrá usarla sin un cast que descarte el calificador, y el compilador no protege el buffer.

❌ Contraejemplo 2 — Copia cuya fuente no es const

void copiar_bloque(void *destino, void *origen, size_t n)
{
    for (size_t i = 0; i < n; i++)
    {
        ((unsigned char *)destino)[i] = ((unsigned char *)origen)[i];
    }
}

Por qué falla: el origen es de sólo lectura, pero su parámetro no lo declara. Además del contrato débil, la firma no permite pasar una fuente const y el compilador no advierte si por error se escribe en origen.

✅ Ejemplo conforme 1 — Lectura con const void *

void imprimir_hex(const void *buffer, size_t n)
{
    const unsigned char *bytes = buffer;
    for (size_t i = 0; i < n; i++)
    {
        printf("%02x ", bytes[i]);
    }
}

El parámetro declara que buffer no se modifica. La conversión a const unsigned char * conserva el calificador y permite recorrer los bytes. El llamador puede pasar cualquier objeto, incluido uno const.

✅ Ejemplo conforme 2 — Origen const y destino mutable

void copiar_bloque(void *destino, const void *origen, size_t n)
{
    unsigned char *salida = destino;
    const unsigned char *entrada = origen;
    for (size_t i = 0; i < n; i++)
    {
        salida[i] = entrada[i];
    }
}

La asimetría de la firma comunica el flujo: el destino se escribe, el origen se lee. Es la misma forma que usa memcpy(restrict void *, const void *, size_t) en la biblioteca estándar.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cParámetro void * sin const en función que sólo lee.
gcc / clanggcc -Wall -Wextra -Wcast-qual -std=c11 ...cast discards 'const' qualifier al convertir el buffer.
cppcheckcppcheck --enable=style archivo.cParameter can be declared as const pointer.

Checklist de autocontrol

Reglas relacionadas