aritmética de punteros void* vs. char*

3 minutos de lectura

avatar de usuario de de1337ed
de1337ed

Estoy mirando mi libro de texto y estoy un poco confundido acerca del código que está ahí. En una parte, están realizando aritmética de punteros de la siguiente manera:

void* bp;
...
bp = (void*)((char*)(bp)+16);
...

pero luego, hacen lo siguiente:

void* bp;
...
bp = bp+16;
...

Siento que deberían ser dos cosas diferentes, pero lo están tratando como la misma cosa. Me siento así porque, por ejemplo, si tuviera que hacer un acceso a una matriz (para una matriz de enteros, por ejemplo), haría lo siguiente

int* a = malloc(n*sizeof(int));
...
q = *(a+1);
...

en este caso, ¿no estoy accediendo a los siguientes 4 bytes en la matriz de enteros y no al siguiente byte? Del mismo modo, siento que si tengo void* a, entonces *(a+1) deberían ser los siguientes 4 bytes… ¿O no es así? Gracias.

  • Ese segundo ejemplo no debería compilarse.

    –Oliver Charlesworth

    7 abr 2012 a las 20:48


  • @OliCharlesworth: no se compilará (o al menos activará una advertencia) si compila en modo conforme. gcc no se ajusta de forma predeterminada e implementa void* la aritmética como una extensión.

    –Keith Thompson

    7 abr 2012 a las 20:54

Es un desliz. aritmética en void * no está definido por el estándar, pero algunos compiladores lo ofrecen como una extensión, comportándose igual que char * para la aritmética. El segundo no es formalmente C válido, pero se deslizó presumiblemente por (mal) hábito.

  • Entonces, ¿la forma correcta de acceder a los siguientes 16 bytes sería primero convertir a char * y luego agregar 16? Lol, tengo que cambiar una buena cantidad de código ahora. Ah, y copié el primero un poco mal, hice un pequeño cambio pero no creo que haga una diferencia en mi pregunta.

    – de1337ed

    07/04/2012 a las 20:50


  • O podrías enviar a uint64_t * y agregue 2;) Sí, la forma portátil es convertir a un puntero a un tipo con tamaño conocido y hacer aritmética sobre eso. Si no necesita portabilidad y su compilador documenta eso void * la aritmética funciona de una manera específica, puedes usar eso. Pero claro, en algún momento tendrás que portar a un compilador diferente…

    –Daniel Fischer

    7 abr 2012 a las 20:53

Avatar de usuario de Antonin GAVREL
Antonin GAVREL

La respuesta aceptada es un buen resumen y quiero dejar más claro por qué usar uno u otro. 😉

Aunque (void *) y (char *) se pueden convertir de manera equivalente a cualquier otro tipo de puntero, solo es legal realizar aritmética de punteros en un (char *) y no con (void *) si desea cumplir con el estándar C.

¿Por qué ambos se usan como punteros? La mayoría de las conversiones de puntero hacia y desde (void *) se pueden realizar sin conversión, pero el uso de (char *) es una reminiscencia de los viejos tiempos.

GCC no advierte sobre la aritmética de punteros en (void *) ya que no es un compilador diseñado para cumplir con el estándar C sino para GNU C. Para cumplir con el estándar C, puede usar las siguientes banderas:

gcc -ansi -pedantic -Wall -Wextra -Werror <more args...>

Mi resultado final:

  • Si quieres para realizar aritmética de punteros: use (char *)
  • Si quieres para obtener la dirección del puntero para convertirlo en otro tipo más adelante: use (void *)

¿Ha sido útil esta solución?