2010-08-02 762 views
8

¿El siguiente código es 100% portátil?Manera alternativa de calcular el tamaño de un tipo usando la aritmética del puntero

int a=10; 
size_t size_of_int = (char *)(&a+1)-(char*)(&a); // No problem here? 

std::cout<<size_of_int;// or printf("%zu",size_of_int); 

P.S: La pregunta es sólo para el aprendizaje de propósito. Así que por favor no dar respuestas como Use sizeof() etc

+0

portátil a qué? int no es independiente de la plataforma y tiene un significado diferente en los sistemas de 64 bits. Aunque mi C++ es probablemente demasiado oxidado para esto. – Blub

+0

@Blub: me refiero a que lo anterior siempre codificará sizeof (int) (por ejemplo: 4 en un entorno típico de 32 bits)? –

+1

para los registros, ¿podría elegir un título apropiado para su pregunta? algo que al menos menciona la aritmética del puntero. gracias –

Respuesta

13

De ANSI-ISO-IEC 14882-2003, p.87 (C++ 03):

"75) Otra manera de acercarse puntero aritmética es primero en convertir el puntero (s) al puntero del personaje (s): En este esquema el valor integral de la expresión suma o se resta de el puntero convertido es primero multiplicado por el tamaño del objeto señaló originalmente para, y la puntero resultante se convierte de nuevo a el tipo original. para puntero resta, el resultado de la diferencia entre el carácter poin ters se divide de manera similar por el tamaño del objeto originalmente apuntado a ".

Esto parece sugerir que la diferencia del puntero es igual al tamaño del objeto.

Si eliminamos la UB'ness de incrementar un puntero a un escalar A y convertir a en una matriz:

int a[1]; 
size_t size_of_int = (char*)(a+1) - (char*)(a); 

std::cout<<size_of_int;// or printf("%zu",size_of_int); 

Luego Esto tiene buen aspecto. Las cláusulas sobre los requisitos de alineación son consistentes con la nota al pie, si los requisitos de alineación son siempre divisibles por el tamaño del objeto.

ACTUALIZACIÓN: Interesante. Como la mayoría de ustedes probablemente sepa, GCC permite especificar una alineación explícita a los tipos como una extensión. Pero no puedo romper método "sizeof" de OP con él porque se niega GCC para su compilación:

#include <stdio.h> 

typedef int a8_int __attribute__((aligned(8))); 

int main() 
{ 
a8_int v[2]; 

printf("=>%d\n",((char*)&v[1]-(char*)&v[0])); 
} 

El mensaje es error: alignment of array elements is greater than element size.

+2

Si reemplaza '& a [1]' con '& a [0] + 1' o' a + 1', bastará con una matriz de tamaño 1, ya que es legal apuntar un extremo al final de una matriz. Además, debe reemplazar '& [0]' con '& a [0]' o 'a' ;-) – fredoverflow

+0

@Fred: ¡Gracias! –

+0

+1 para la respuesta técnicamente correcta. @FredOverflow: en C99 '& a [1]' y 'a + 1' son exactamente iguales. :) –

1

¿Por qué no:

size_t size_of_int = sizeof(int); 
+2

Eso no responde mi pregunta. Sé que tenemos el operador 'sizeof()' para tal fin. La pregunta es solo para aprender. :) –

4

&a+1 dará lugar a un comportamiento no definido de acuerdo con el estándar de C++ 5,7/5:

Cuando una expresión que tiene el tipo integral agregado o restado de un puntero, el resultado tiene el tipo de el operando del puntero. < ...> Si tanto el operando del puntero como el resultado apuntan a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz, la evaluación no producirá un desbordamiento; ; de lo contrario, el comportamiento no está definido.

&a+1 está bien de acuerdo con 5,7/4:

Para los fines de estos operadores, un puntero a un objeto nonarray se comporta igual que un puntero a la primera elemento de una matriz de longitud uno con el tipo del objeto como su tipo de elemento.

Eso significa que 5.7/5 se puede aplicar sin UB. Y finalmente la observación 75 de 5.7/6 como @Luther Blissett señalado en su respuesta dice que el código en la pregunta es válido.


En el código de producción, debe usar sizeof en su lugar. Pero el Estándar C++ no garantiza que sizeof(int) resultará en 4 en cada plataforma de 32 bits.

+0

Es IMHO válido según 'Si tanto el operando del puntero como el resultado apuntan a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz, ...' The '& a + 1' puntero puntos uno pasado el final que es puntero válido y utilizable. – wilx

+0

Debe reemplazar '& a [1]' con '& a [0] + 1' o' a + 1', porque el primero puede generar [comportamiento indefinido] (http://stackoverflow.com/questions/3144904/may -i-toma-la-dirección-del-uno-pasado-el-elemento-final-de-una-matriz-cerrado). – fredoverflow

0

El código anterior calculará portablemente sizeof(int) en una plataforma de destino, pero este último es la implementación definida - obtendrá diferentes resultados en diferentes plataformas.

1

Probablemente se haya definido la implementación.

Puedo imaginar un sistema (hipotético) donde sizeof (int) es más pequeño que la alineación predeterminada.

se ve sólo es seguro decir que size_of_int >= sizeof(int)

0

Sí, le da el equivalente de sizeof(a) pero utilizando en lugar de ptrdiff_tsize_t tipo.

0

Hubo un debate en un similar question.

Consulte los comentarios en my answer a esa pregunta para algunos consejos sobre por qué esto no solo no es portátil, sino que también es un comportamiento indefinido según la norma.

2

No. Este código no funcionará como espera en cada plataforma. Al menos en teoría, puede haber una plataforma con, por ejemplo, Números de 24 bits (= 3 bytes) pero alineación de 32 bits. Tales alineaciones no son atípicas para plataformas (más antiguas o más simples). Entonces, su código devolvería 4, pero sizeof (int) devolvería 3.

Pero no conozco un hardware real que se comporte de esa manera. En la práctica, su código funcionará en la mayoría o en todas las plataformas.

+2

-1, en tales plataformas 'sizeof (int)' es 4. Sin embargo, 8 de esos 32 bits no tomarán parte en la representación del valor de int. Solo para char (sin signo) obtiene la garantía de que todos los bits participan en la representación del valor. (Tiene que hacerlo, de lo contrario no podría memcpy cosas). – MSalters

2

No es 100% portátil por las siguientes razones:

  1. Editar: usaría mejor int a[1]; y luego a+1 se convierte definitivamente válida.
  2. & a invoca comportamiento indefinido en los objetos de la clase de almacenamiento de registros.
  3. En caso de restricciones de alineación que sean mayores o iguales que el tamaño de int tipo, size_of_int no contendrá la respuesta correcta.

responsabilidad:

no estoy seguro si la bodega de arriba para C++.

+0

+1 para la nota sobre UB y regístrese. Aunque si está utilizando el registro (por buenas razones), sin duda conoce el tamaño y las preocupaciones relacionadas con la portabilidad. –

+0

Nunca es UB. En C está mal formado (requiere diagnóstico) y en C++ está permitido (los compiladores sicne lo ignoraron de todos modos). – MSalters

+0

@MSalters: ¿Supongo que estabas hablando de (1)? De hecho, de hecho, después de referirse al estándar, de acuerdo con la semántica de los operadores aditivos, está bien. No puedo encontrar dónde se considera mal formado. ¿Puede usted ayudar? –

Cuestiones relacionadas