Aritmética de puntero para el puntero de vacío en C

Cuando se incrementa un puntero a un tipo particular (digamos int , char , float , ..), su valor aumenta por el tamaño de ese tipo de datos. Si se incrementa un puntero de void que apunta a datos de tamaño x , ¿cómo llega a apuntar x bytes adelante? ¿Cómo sabe el comstackdor agregar x al valor del puntero?

    Conclusión final: la aritmética en un void* es ilegal tanto en C como en C ++.

    GCC lo permite como una extensión, consulte Aritmética en void – y Punteros de función (tenga en cuenta que esta sección es parte del capítulo “Extensiones C” del manual). Clang e ICC probablemente permitan la aritmética void* con fines de compatibilidad con GCC. Otros comstackdores (como MSVC) no permiten la aritmética en void* , y GCC la deshabilita si se especifica el -pedantic-errors , o si se especifica el -Werror-pointer-arith (este indicador es útil si su base de código también debe comstackr con MSVC).

    El estándar C habla

    Las citas se toman del borrador n1256.

    La descripción de la norma de la operación de adición indica:

    6.5.6-2: para la sum, ambos operandos deben tener un tipo aritmético, o un operando debe ser un puntero a un tipo de objeto y el otro debe tener un tipo entero.

    Entonces, la pregunta aquí es si void* es un puntero a un “tipo de objeto”, o lo que es lo mismo, si void es un “tipo de objeto”. La definición para “tipo de objeto” es:

    6.2.5.1: Los tipos se dividen en tipos de objetos (tipos que describen completamente los objetos), tipos de funciones (tipos que describen funciones) y tipos incompletos (tipos que describen objetos pero carecen de la información necesaria para determinar sus tamaños).

    Y el estándar define el void como:

    6.2.5-19: el tipo de void comprende un conjunto vacío de valores; es un tipo incompleto que no puede completarse.

    Como el void es un tipo incompleto, no es un tipo de objeto. Por lo tanto, no es un operando válido para una operación de sum.

    Por lo tanto, no puede realizar aritmética de puntero en un puntero de void .

    Notas

    Originalmente, se pensó que se permitía la aritmética void* debido a estas secciones del estándar C:

    6.2.5-27: Un puntero a vacío tendrá los mismos requisitos de representación y alineación que un puntero a un tipo de carácter.

    Sin embargo,

    Los mismos requisitos de representación y alineación implican intercambiabilidad como argumentos para funciones, valores de retorno de funciones y miembros de uniones.

    Esto significa que printf("%s", x) tiene el mismo significado si x tiene tipo char* o void* , pero eso no significa que se pueda hacer aritmética en un void* .

    Nota del editor: esta respuesta ha sido editada para reflejar la conclusión final.

    La aritmética del puntero no está permitida en los punteros void* .

    lanzarlo a un puntero char e incrementar su puntero hacia adelante x bytes adelante.

    ¡No se puede hacer una aritmética de punteros en los tipos void * , exactamente por esta razón!

    Tienes que convertirlo en otro tipo de puntero antes de realizar la aritmética del puntero.

    Los punteros vacíos pueden señalar cualquier fragmento de memoria. Por lo tanto, el comstackdor no sabe cuántos bytes incrementar / disminuir cuando intentamos la aritmética del puntero en un puntero void. Por lo tanto, los punteros vacíos deben ser primero encasillados a un tipo conocido antes de que puedan estar involucrados en cualquier aritmética del puntero.

     void *p = malloc(sizeof(char)*10); p++; //compiler does how many where to pint the pointer after this increment operation char * c = (char *)p; c++; // compiler will increment the c by 1, since size of char is 1 byte. 

    El estándar C no permite la aritmética del puntero nulo . Sin embargo, GNU C está permitido al considerar que el tamaño del vacío es 1 .

    C11 estándar §6.2.5

    Párrafo – 19

    El tipo de void comprende un conjunto vacío de valores; es un tipo de objeto incompleto que no se puede completar.

    El siguiente progtwig funciona bien en el comstackdor de GCC.

     #include int main() { int arr[2] = {1, 2}; void *ptr = &arr; ptr = ptr + sizeof(int); printf("%d\n", *(int *)ptr); return 0; } 

    Puede ser que otros comstackdores generen un error.

    El comstackdor sabe por tipo de molde. Dado un void *x :

    • x+1 agrega un byte a x , el puntero va al byte x+1
    • (int*)x+1 agrega sizeof(int) bytes, el puntero va al byte x + sizeof(int)
    • (float*)x+1 addres sizeof(float) bytes, etc.

    Aunque el primer elemento no es portátil y está en contra del Galateo de C / C ++, sin embargo es C-language-correct, lo que significa que comstackrá algo en la mayoría de comstackdores que posiblemente necesite una bandera apropiada (como -Wpointer-arith)