c++ - que - punteros en c ejemplos
¿Cuándo y cómo se permite la conversión a puntero char? (7)
Podemos ver la representación de un objeto de tipo T
convirtiendo un T*
que apunta a ese objeto en un char*
. Al menos en la práctica:
int x = 511;
unsigned char* cp = (unsigned char*)&x;
std::cout << std::hex << std::setfill(''0'');
for (int i = 0; i < sizeof(int); i++) {
std::cout << std::setw(2) << (int)cp[i] << '' '';
}
Esto genera la representación de 511
en mi sistema: ff 01 00 00
.
Hay (seguramente) algún comportamiento de implementación definido que está ocurriendo aquí. ¿Cuál de los lanzamientos me permite convertir un int*
en un unsigned char*
y qué conversiones implica ese lanzamiento? ¿Estoy invocando un comportamiento indefinido tan pronto como lance? ¿Puedo lanzar cualquier tipo de T*
como este? ¿En qué puedo confiar al hacer esto?
¿Cuál de los lanzamientos me está permitiendo convertir un
int*
a ununsigned char*
?
Ese reparto de estilo C en este caso es el mismo que reinterpret_cast<unsigned char*>
.
¿Puedo lanzar cualquier tipo de T * como este?
Si y no. La parte sí: puede lanzar de forma segura cualquier tipo de puntero a un char*
o unsigned char*
(con los calificadores const
y / o volatile
apropiados). El resultado está definido por la implementación, pero es legal.
La parte "no": el estándar permite explícitamente a los caracteres char*
y unsigned char*
como el tipo de destino. Sin embargo, no puede (por ejemplo) convertir de forma segura un double*
a un int*
. Haga esto y habrá cruzado el límite del comportamiento definido por la implementación al comportamiento indefinido. Viola la estricta regla de aliasing.
A menos que tenga un operador de cast, entonces un cast es simplemente indicando que "vea" esa área de memoria de una manera diferente. Nada realmente lujoso, diría yo.
Entonces, estás leyendo el área de memoria byte por byte; Mientras no lo cambies, está bien. Por supuesto, el resultado de lo que se ve depende mucho de la plataforma: piense en la endianidad, el tamaño de la palabra, el relleno, etc.
El comportamiento de implementación en su ejemplo es el atributo endianness de su sistema, en este caso, su CPU es un poco endian.
Acerca de la conversión de tipos, cuando lanzas un int*
to char*
todo lo que estás haciendo es decirle al compilador que interprete lo que cp
apunta como un char, por lo que solo leerá el primer byte y lo interpretará como un carácter.
El reparto de estilo C en este caso es equivalente a reinterpret_cast. El Estándar describe la semántica en 5.2.10. Concretamente, en el apartado 7:
"Un puntero a un objeto se puede convertir explícitamente a un puntero a un tipo de objeto diferente.70 Cuando un prvalue v de tipo" puntero a T1 "se convierte al tipo" puntero a cvT2 ", el resultado es
static_cast<cvT2*>(static_cast<cvvoid*>(v))
si T1 y T2 son tipos de diseño estándar (3.9) y los requisitos de alineación de T2 no son más estrictos que los de T1. Convertir un prvalor de tipo "puntero a T1" al tipo "Puntero a T2" (donde T1 y T2 son tipos de objetos y donde los requisitos de alineación de T2 no son más estrictos que los de T1) y de vuelta a su tipo original, se obtiene el valor del puntero original. El resultado de cualquier otra conversión de puntero no se especifica . "
Lo que significa en su caso, se cumplen los requisitos de alineación y el resultado no está especificado.
El reparto entre los punteros siempre es posible, ya que todos los punteros no son más que direcciones de memoria y cualquier tipo de memoria, siempre se puede pensar como una secuencia de bytes.
Pero, por supuesto, la forma en que se forma la secuencia depende de cómo se represente el tipo descompuesto en la memoria, y eso está fuera del alcance de las especificaciones de C ++.
Dicho esto, a menos que se trate de casos muy patológicos, puede esperar que la representación sea la misma en todo el código producido por un mismo compilador para todas las máquinas de una misma plataforma (o familia), y no debe esperar los mismos resultados en diferentes plataformas .
En general, una cosa que se debe evitar es expresar la relación entre los tamaños de tipo como "predefinidos": en su muestra, asume sizeof(int) == 4*sizeof(char)
: eso no es necesariamente siempre cierto.
Pero siempre es cierto que sizeof (T) = N * sizeof (char), por lo tanto, cualquier T siempre se puede ver como un número entero de char-s
Simplemente invierte el orden de los bytes y se convierte en
00 00 01 ff
Lo que es 256 (01) + 255 (ff) = 511
Esto se debe a que su plataforma es poco endian.
Su reparto se asigna a:
unsigned char* cp = reinterpret_cast<unsigned char*>(&x);
La representación subyacente de un int
está definida por la implementación, y verla como caracteres le permite examinarla. En tu caso, es 32 bit little endian.
No hay nada especial aquí: este método de examinar la representación interna es válido para cualquier tipo de datos.
C ++ 03 5.2.10.7: Un puntero a un objeto se puede convertir explícitamente en un puntero a un objeto de diferente tipo. Excepto que la conversión de un valor de tipo "puntero a T1" al tipo "puntero a T2" (donde T1 y T2 son tipos de objeto y donde los requisitos de alineación de T2 no son más estrictos que los de T1) y regresa a sus rendimientos de tipo originales el valor del puntero original, el resultado de tal conversión de puntero no se especifica.
Esto sugiere que el reparto da como resultado un comportamiento no especificado . Pero hablando pragmáticamente, la conversión de cualquier tipo de puntero a char*
siempre le permitirá examinar (y modificar) la representación interna del objeto referenciado.