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?
Aritmética de punteros para punteros vacíos en C
Siva Shankaran
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
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
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
Jakob
Tienes que lanzarlo a otro tipo de puntero antes de hacer aritmética de punteros.
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ñox
se incrementa, ¿cómo llega al puntox
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