2010-10-25 25 views
12

es válida esta¿Puedo hacer aritmética en punteros void * en C?

void *p = &X; /* some thing */ 
p += 12; 

y si es así ¿qué p ahora apuntar a? Tengo un código de (tercero) que hace esto (y compila limpiamente) y mi suposición es que el vacío * fue tratado como un char *. Mi fiel K & R es silenciosa (ish) sobre el tema

EDITAR: Mi pequeña aplicación de prueba funciona bien en gcc 4.1.1 y trata void * como char *. Pero g ++ barfs

Sé cómo hacerlo correctamente. Necesito saber si tengo que limpiar esta base de código para encontrar todos los lugares en los que está hecho.

Por cierto GCC -pedantic lanza una advertencia

Resumen:

especificación

El C es ambiguo. Dice que en términos de representación y uso como parámetros de función void * = char *. Pero no dice nada sobre la aritmética del puntero.

  • gcc (4) lo permite y lo trata como char *
  • g ++ rechaza
  • -pedantic gcc advierte de ello
  • VS2010 tanto C y C++ rechaza
+1

relacionados? http://stackoverflow.com/questions/1997751/c-gcc-compiler-options-for-pointer-arithmeticwarning – pmg

Respuesta

4

Depende del compilador. Aquellos que lo permiten consideran sizeof (* (void *)) como 1.

EDITAR: es solo para aritmética de punteros vacíos. No tendría sentido usar en este caso los pasos de sizeof (int) o de 0. Las expectativas comunes de alguien que lo usa sería el menor paso posible.

+1

¿Qué compilador permite esto? Eso implica que 'sizeof (void) == 1' que es ... no cuerdo. – JaredPar

+23

Tal vez por pequeños valores de 1. –

+4

@Jason, gracias por desperdiciar unos minutos de mi día mientras estaba sentado en mi silla sin poder reír :) – JaredPar

14

No, esto no es legal. No se puede aumentar arbitrariamente un void*. Primero se debe convertir a un tipo específico.

Si desea incrementarlo por un número específico de bytes, esta es la solución que uso.

p = ((char*)p) + 12; 

El tipo char es conveniente ya que tiene un tamaño definido de 1 byte.

EDITAR

Es interesante que se ejecuta en gcc con una advertencia. Probé en Visual Studio 2010 y verifiqué que no compila. Mi comprensión limitada del estándar diría que gcc en el error aquí. Puede agregar los siguientes indicadores de compilación

-Wall -ansi -pedantic 
+1

Visual Studio compila el código como C++ de forma predeterminada, por lo que arroja un error. – casablanca

+0

-pedantic lanza una advertencia. compilando como C++ fatales – pm100

+0

@casablanca Todavía estoy desconcertado sobre cómo el incremento 'void *' podría ser legal en C en absoluto. Ciertamente no parece portátil. – JaredPar

1

No creo que pueda, porque no conoce su tipo, por lo tanto, no puede buscar la cantidad correcta de bytes.

Primero colóquela en un tipo, es decir, (int).

+0

Si se lanza a 'int', entonces el' + = 12' se interpreta esencialmente como '+ = sizeof (int) * 12' en términos de cuántos bytes se agregaron realmente. Esto no parece ser el comportamiento previsto. – JaredPar

+0

@JaredPar ¿Funcionaría si ese puntero apuntara a un número entero? – alex

+0

nuevamente depende de la intención. Si la intención era moverlo 12 elementos hacia adelante, entonces sí. Si la intención era moverlo 12 bytes hacia adelante, entonces no. Difícil leer el intento de la pregunta, pero mi suposición es que el OP no desea incrementos de 12 bytes. – JaredPar

2

Supongo que es correcto.

En la norma ISO C99, sección 6.2.5 párrafo 26, declara que los punteros vacíos y los punteros de caracteres tendrán los mismos requisitos de representación y alineación (parafraseo).

+2

También dice en §6.5.6 párrafo 2 que ambos operandos tienen tipos aritméticos o un operando tiene un tipo entero y el otro operando es un puntero a un tipo de objeto (un puntero a vacío no es un puntero a un tipo de objeto). Entonces, la aritmética en un puntero al vacío es * no * definida. – dreamlax

+0

@dreamlax: Y ** no definido ** significa que el compilador puede hacer lo que quiera, incluso usar un tamaño de elemento de '1'. –

+0

@dreamlax Sí, de acuerdo. No entendí la nota al pie 39. En 6.3.2.3, párrafo 1, dice que el vacío * puede convertirse a/de cualquier cosa con impunidad. Dado que se requiere que tenga las mismas características que char *, parece lógico que el compilador lo convierta implícitamente en un char *. Dicho esto, creo que es una "muy mala práctica" manipular el vacío * de esta manera. –

12

Para citar de la especificación:

§6.5.6/2: Para la adición, ya sea ambos operandos tendrá tipo aritmético, o un operando debe ser un puntero a un tipo de objeto y el otro tendrá un tipo entero. (Incremental es equivalente a la adición de 1.)

Un puntero a vacío no es un puntero a un tipo de objeto, como por estos extractos:

§6.2.5/1: [. ..] Los tipos están divididos en tipos de objetos (tipos que describen completamente objetos), tipos de funciones (tipos que describen funciones) y tipos incompletos (tipos que describen objetos pero carecen de información necesaria para determinar sus tamaños).

§6.2.5/19: El tipo de vacío comprende un conjunto vacío de valores; es un tipo incompleto que no puede ser completado.

Por lo tanto, la aritmética de punteros se no define de puntero para anular tipos.

+0

+1 para señalar que la especificación C no es ambigua en esto. Añadiría que, dado que 6.5.6/2 es parte de una sección de "Restricciones", este no es un comportamiento indefinido, sino que se requiere un compilador conforme para emitir un diagnóstico (5.1.1.3/1). En el caso de 'gcc -pedantic', solo una advertencia, pero un diagnóstico de todos modos. –