Aritmética de punteros para punteros vacíos en C

7 minutos de lectura

avatar de usuario
Siva Shankaran

Cuando un apuntador a un tipo particular (digamos int, char, float, ..) se incrementa, su valor se incrementa por el tamaño de ese tipo de datos. si un void puntero que apunta a datos de tamaño x se incrementa, ¿cómo llega al punto x bytes por delante? ¿Cómo sabe el compilador que debe sumar x al valor del puntero?

  • posible duplicado de Error al realizar aritmética de puntero en vacío * en MSVC

    –Carl Norum

    19 de agosto de 2010 a las 21:43

  • La pregunta suena como si supusiera que el compilador (/ tiempo de ejecución) sabe en qué tipo de objeto se configuró el puntero y agrega su tamaño al puntero. Eso es un completo error: solo conoce la dirección.

    – PJ Trail

    5 mayo 2015 a las 21:22

  • “Si un void puntero que apunta a datos de tamaño x se incrementa, ¿cómo llega al punto x bytes adelante?” No es así. ¿Por qué las personas que tienen tales preguntas no pueden probarlas antes de preguntar? Ya sabes, al menos al mínimo donde verifican si realmente compila, que esto no lo hace. -1, No puedo creer que esto tenga +100 y -0.

    – subrayado_d

    20 de agosto de 2016 a las 6:30

avatar de usuario
Sadeq

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

GCC lo permite como una extensión, ver 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 void* aritmética a efectos de compatibilidad con GCC. Otros compiladores (como MSVC) no permiten la aritmética en void*y GCC no lo permite si el -pedantic-errors se especifica la bandera, o si el -Werror-pointer-arith se especifica el indicador (este indicador es útil si su base de código también debe compilarse con MSVC).

El estándar C habla

Las citas se toman del borrador n1256.

La descripción estándar de la operación de adición establece:

6.5.6-2: Para la suma, 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 de manera equivalente, ya sea void es un “tipo de objeto”. La definición de “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 la norma define void como:

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

Ya que 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 suma.

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

notas

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

6.2.5-27: Un apuntador a nulo debe tener la misma representación y alineación
requisitos como un puntero a un tipo de carácter.

Sin embargo,

La misma representación y alineación.
Los requisitos están destinados a implicar intercambiabilidad como argumentos para funciones, valores de retorno de funciones y miembros de uniones.

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

  • Del estándar C99: (6.5.6.2) Para la suma, ambos operandos tendrán un tipo aritmético, o un operando será un puntero a un tipo de objeto y el otro tendrá un tipo entero. (6.2.5.19) El tipo void comprende un conjunto vacío de valores; es un tipo incompleto que no se puede completar. Creo que eso deja claro que void* no se permite la aritmética de punteros. GCC tiene un extensión que permite hacer esto.

    – mtvec

    19 de agosto de 2010 a las 18:29

  • si ya no cree que su respuesta sea útil, simplemente puede eliminarla.

    – café

    20 de agosto de 2010 a las 1:28

  • Esta respuesta fue útil a pesar de que resultó incorrecta, ya que contiene una prueba concluyente de que los punteros vacíos no están destinados a la aritmética.

    – Ben Flynn

    17/10/2012 a las 21:48

  • Esta es una buena respuesta, tiene la conclusión correcta y las citas necesarias, pero las personas que respondieron a esta pregunta llegaron a una conclusión incorrecta porque no leyeron hasta el final de la respuesta. He editado esto para hacerlo más obvio.

    -Dietrich Epp

    11 de mayo de 2013 a las 2:37

  • Ah, pero para la suma de punteros, ahora “un operando será un puntero a un tipo de objeto completo y el otro tendrá un tipo entero”. Así que supongo que la adición de punteros con un puntero void* sigue siendo un comportamiento indefinido.

    –Steve Siegel

    14 de diciembre de 2016 a las 1:55

avatar de usuario
msc

Él estándar C no permite vacío aritmética de punteros. Sin embargo, GNU C se permite teniendo en cuenta el tamaño de vacío es 1.

C11 estándar §6.2.5

Párrafo – 19

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

El siguiente programa funciona bien en el compilador GCC.

#include<stdio.h>

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 compiladores generen un error.

Los punteros vacíos pueden apuntar a cualquier fragmento de memoria. Por lo tanto, el compilador no sabe cuántos bytes incrementar/decrementar cuando intentamos la aritmética de punteros en un puntero vacío. Por lo tanto, los punteros vacíos deben encasillarse primero en un tipo conocido antes de que puedan participar en cualquier aritmética de punteros.

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.

cámbielo a un puntero de caracteres e incremente su puntero hacia adelante x bytes adelante.

La aritmética de punteros no está permitida en void* punteros

  • +1 La aritmética de punteros solo se define para punteros a (completar) tipos de objetos. void es un tipo incompleto que nunca puede ser completada por definición.

    – tiro

    19 de agosto de 2010 a las 15:33

  • @schot: Exacto. Además, la aritmética de punteros solo se define en un puntero a un elemento de un objeto de matriz y solo si el resultado de la operación sería un puntero a un elemento en esa misma matriz o uno más allá del último elemento de esa matriz. Si esas condiciones no se cumplen, es un comportamiento indefinido. (Del estándar C99 6.5.6.8)

    – mtvec

    19 de agosto de 2010 a las 18:39


  • Aparentemente no es así con gcc 7.3.0. El compilador acepta p + 1024, donde p es nulo*. Y el resultado es el mismo que ((char *)p) + 1024

    – uuu777

    24 de marzo de 2019 a las 2:27

  • @zzz777 es una extensión GCC, vea el enlace en la respuesta más votada.

    – Ruslán

    3 abr 2020 a las 18:48

avatar de usuario
Oliver Charlesworth

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

  • +1 La aritmética de punteros solo se define para punteros a (completar) tipos de objetos. void es un tipo incompleto que nunca puede ser completada por definición.

    – tiro

    19 de agosto de 2010 a las 15:33

  • @schot: Exacto. Además, la aritmética de punteros solo se define en un puntero a un elemento de un objeto de matriz y solo si el resultado de la operación sería un puntero a un elemento en esa misma matriz o uno más allá del último elemento de esa matriz. Si esas condiciones no se cumplen, es un comportamiento indefinido. (Del estándar C99 6.5.6.8)

    – mtvec

    19 de agosto de 2010 a las 18:39


  • Aparentemente no es así con gcc 7.3.0. El compilador acepta p + 1024, donde p es nulo*. Y el resultado es el mismo que ((char *)p) + 1024

    – uuu777

    24 de marzo de 2019 a las 2:27

  • @zzz777 es una extensión GCC, vea el enlace en la respuesta más votada.

    – Ruslán

    3 abr 2020 a las 18:48

avatar de usuario
Jakob

Tienes que lanzarlo a otro tipo de puntero antes de hacer aritmética de punteros.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad