C++: Es seguro elenco puntero a int y más tarde de nuevo a puntero de nuevo?

Es seguro para lanzar el puntero a int y más tarde de nuevo a puntero de nuevo?

¿Y si sabemos que es el puntero de 32 bits de largo y de tipo int es de 32 bits de largo?

long* juggle(long* p) {
    static_assert(sizeof(long*) == sizeof(int));
    int v = reinterpret_cast<int>(p); //or if sizeof(*)==8 choose long here
    do_some_math(v); //prevent compiler from optimizing
    return reinterpret_cast<long*>(v);
}

int main() {
    long* stuff = new long(42);
    long* ffuts = juggle(stuff); 
    std::cout << "Is this always 42? " << *ffuts << std::endl;
}

Es esta cubierto por la Norma?

  • Por qué no echa a void*
  • ¿Por qué quieres? ¿Qué se puede hacer con un int que usted no puede hacer con un puntero?
  • posibles duplicados de sizeof (int) == sizeof (void*)?
  • esta pregunta va más allá de sizeof
  • con la edición, y el ejemplo más, se convirtió en una cuestión diferente… me tome mi voto de nuevo.
  • No sé acerca de C++, pero en C es seguro para lanzar un puntero a int. La única int es seguro (sobre todo compatible con C plataformas) para la conversión a un puntero (y lograr «definido» resultados) es cero (null). Lo sé porque ca 1990 tuvimos un compilador de C que tenía la punta de los pies alrededor de esta certificación problema.
  • En C no siempre es seguro para lanzar un puntero a int, tampoco. «Un puntero puede ser convertido a un tipo integral. El tamaño de los enteros requerido y el resultado es una aplicación definida. Si el espacio disponible no es lo suficientemente largo, el comportamiento es indefinido» (ISO C90 6.3.4).
  • Entonces, ¿qué comportamiento inseguro puede ocurrir como resultado? (Pérdida de precisión no es «peligroso».)
  • El comportamiento peligroso que puede ocurrir es el peor de todos ellos: un comportamiento indefinido. (En la práctica, sin embargo, sólo una pérdida de precisión va a ocurrir, pero esto es suficiente para causar un fallo de segmentación si el entero se echa a un puntero y, a continuación, se eliminan las referencias.)
  • Pero casting entero puntero no es seguro, excepto en circunstancias limitadas. Casting puntero a entero, incluso cuando se produce un truncamiento, es a veces útil para la generación de valores de hash, etc, y por lo tanto, mientras que la transformación no está «definido», no es «peligroso».
  • Una vez más, en la práctica, usted está en lo correcto y esto no es un problema con la mayoría, si no todos, de los compiladores y procesadores, pero con respecto a las normas, que es lo que la pregunta es, un comportamiento indefinido es definitivamente comportamiento inseguro. Un programa que invoca a la UB no es compatible con el programa, y, bajo la cobertura de la norma ISO 9899, ninguna afirmación puede ser hecho con respecto a su comportamiento. Por ejemplo, la conversión de un puntero a un (firmado) entero podría desbordamiento de entero (que es otra UB), meterse con algo de relleno de bits y crear una trampa de la representación.
  • No puedo hablar por la precisión de los términos de la norma actual, pero ca de 1990, cuando estábamos ayudando a conseguir un compilador de C certificado, el comportamiento de puntero a int no fue considerado como «peligroso», y que era necesario para ser compatibles (aunque la asignación no está definido). Va para otro lado, sólo el único valor cero debía ser «definido»/compatible. Esto surgió debido a que, en el Sistema/38, punteros eran en realidad «capacidades» que contiene un 48 bits de dirección y una de 1 bit «etiqueta». El «tag», era inaccesible para el software, y fue perdido necesariamente la fundición a la int. Tanto el estudio de la norma se produjo.
  • Que requieren de un compilador para el apoyo de un comportamiento indefinido de la construcción es, sin duda excesiva; sin embargo, para entender mejor la situación en la que hemos mencionado, a poco más de contexto debe ser proporcionada. Sin embargo, tenga en cuenta que un adecúa compilador («aplicación» en el estándar de la jerga) pueden elegir activamente definir, e incluso documento, cualquier UB condición como deseo, como si se tratara de algún tipo de extensión o aplicación definida por el comportamiento; un adecúa programa, sin embargo, simplemente no se puede confiar en este tipo de conducta, ni siquiera por accidente, poniendo su «adaptabilidad» en riesgo si lo hace.

InformationsquelleAutor hasdf | 2010-08-25

12 Kommentare

  1. 37

    No.

    Por ejemplo, en los sistemas x86-64, que es un puntero de 64 bits de largo, pero int es sólo de 32 bits de largo. La fundición de un puntero a int y de nuevo hace que la parte superior de 32 bits del puntero de valor perdido.

    Usted puede utilizar el intptr_t tipo en <cstdint> si desea un tipo entero, que está garantizado para ser tan largo como el puntero. Usted puede de manera segura reinterpret_cast de un puntero a un intptr_t y la espalda.

    • Bien, es seguro para lanzar si se sabe que es el puntero de 32 bits de largo?
    • mi entendimiento es que sólo el mínimo rangos estandarizados. Para Visual C++ por ejemplo, el tamaño de int es el mismo en 32 y 64 bit en plataformas de 64 bits int se define como __int64. Consulte msdn.microsoft.com/en-us/library/296az74e(VS.80).aspx para obtener más detalles.
    • técnicamente me imagino que dependerá de la aplicación, pero me gustaría esperar más implemenatations para hacer lo que quieres si los tamaños, donde el mismo. lo que sin duda puede hacer es arrojado a/de un adecuado tamaño de un array de caracteres. por supuesto, por qué quieres hacer esto también es importante porque hay podría ser incluso más UB acechando en lo que usted está tratando de usar este para.
    • Bien, entonces el límite de la portabilidad de la aplicación. @RichN supongo sizeof(int) depende del compilador – consulte stackoverflow.com/questions/589575/c-size-of-int-long-etc
    • No, la norma especifica que usted obtenga el valor original de la espalda si el tipo integer es lo suficientemente grande. El único problema es encontrar un tipo lo suficientemente grande.
    • El uso de un intptr_t si usted realmente necesita para lanzar a una aritmética tipo.
    • +1 para el «No». jaja

  2. 22

    Sí, si… (o «Sí, pero…») y no de otra manera.

    La norma especifica (3.7.4.3) los siguientes:

    • Un valor de puntero es una forma segura de derivados de puntero […] si es el resultado de una bien definida puntero de conversión o reinterpret_cast de una forma segura derivados de puntero de valor [o] el resultado de un reinterpret_cast de un entero representación de una forma segura derivados de puntero de valor
    • Un valor de número entero es un número entero representación de una forma segura derivados de puntero […] si su tipo es al menos tan grande como std::intptr_t y […] el resultado de un reinterpret_cast de una forma segura derivados de puntero de valor [o]
      el resultado de un acuerdo válido de la conversión de un entero representación de una forma segura derivados de puntero de valor [o] el resultado de un aditivo o de la operación bit a bit, uno de cuyos operandos es un entero representación de un
      seguridad derivados de puntero de valor
    • Una trazabilidad puntero de objeto […] un objeto de tipo integral, que es al menos tan grande como std::intptr_t

    La norma establece además que las implementaciones de puede ser relajado o puede ser estricto sobre la aplicación de seguridad derivados de los punteros. Lo que significa que no se ha especificado si el uso o eliminar un no-seguridad derivados de puntero invoca un comportamiento indefinido (que es una cosa divertida de decir!)

    Que en resumen significa nada más y nada menos que «algo diferente podría trabajo de todos modos, pero la única cosa segura es la que se especifica arriba».

    Por lo tanto, si utiliza std::intptr_t en el primer lugar (el de la copa de cosa que hacer!) o si usted sabe que el tamaño de almacenamiento de cualquier tipo de entero de utilizar (por ejemplo, long) es de al menos el tamaño de std::intptr_t, entonces es permisible y bien definida (es decir, «seguro») para la conversión a su tipo entero y la espalda. La norma garantiza que.

    Si ese no es el caso, la conversión de puntero a entero representación probablemente (o, al menos, posiblemente, perder algo de información, y la conversión de la espalda no le dará un puntero válido. O, podría por accidente, pero esto no está garantizado.

    Una anécdota interesante es que el estándar de C++ no directamente definir std::intptr_t en absoluto; simplemente dice «el mismo que 7.18 en el estándar de C».

    El estándar de C, por otro lado, los estados «designa a un entero con signo tipo con la propiedad de que cualquier válido
    puntero a void, puede ser convertido a este tipo, a continuación, volver a convertirse puntero a void, y el resultado se compara igual a la original puntero»
    .

    Lo que significa que, sin la más complicada de las definiciones anteriores (en particular, el último bit de la primera viñeta), no sería admisible para convertir a/de cualquier cosa, pero void*.

    • Esta es una fuerte evidencia
  3. 21

    Sí y no.

    La especificación del lenguaje declara explícitamente que es seguro (lo que significa que en la final se consigue el original valor de puntero) mientras el tamaño del tipo integral, es suficiente para almacenar el [dependiente de la implementación] representación integral del puntero.

    Así, en el caso general no es «seguro», ya que en el caso general int puede fácilmente llegar a ser demasiado pequeño. En su caso concreto, aunque podría ser seguro, ya que su int podría ser lo suficientemente grande para almacenar el puntero.

    Normalmente, cuando usted necesita para hacer algo como eso, debe utilizar la intptr_t/uintptr_t tipos, que son específicamente introducido para ese propósito. Por desgracia, intptr_t/uintptr_t no son parte de la actual estándar de C++ (que son estándar C99 tipos), pero muchos de proporcionar implementaciones de ellos, sin embargo. Siempre se puede definir estos tipos, por supuesto.

  4. 5

    En general, no; los punteros pueden ser más grandes que int, en cuyo caso no hay manera de reconstruir el valor.

    Si un número entero es conocido por ser lo suficientemente grande, entonces usted puede; de acuerdo a la Norma (5.2.10/5):

    Un puntero se convierte a un entero de tamaño suficiente … y de nuevo para el mismo tipo de puntero tendrá su valor original

    Sin embargo, en C++03, no hay una manera estándar de decir que los tipos enteros son lo suficientemente grandes. C++11 y C99 (y, por tanto, en la práctica la mayoría de los C++03 implementaciones), y también Aumentar.Entero, definir intptr_t y uintptr_t para este propósito. O usted puede definir su propio tipo, y afirman (preferentemente en tiempo de compilación) que es lo suficientemente grande; o, si usted no tiene alguna razón especial para que sea de un tipo entero, el uso de void*.

  5. 3

    Es seguro? De verdad que no.

    En la mayoría de las circunstancias, va a funcionar? Sí

    Ciertamente, si un int es demasiado pequeño para contener el pleno valor de puntero y trunca, no recibirá su original puntero (es de esperar que su compilador advertir acerca de este caso, con GCC truncar las conversiones de puntero a enteros son errores de disco). Un long, o uintptr_t si su biblioteca admite, pueden ser mejores opciones.

    Incluso si su tipo entero y los tipos de puntero son del mismo tamaño, no necesariamente el trabajo en función de la aplicación en tiempo de ejecución. En particular, si está utilizando un recolector de basura en su programa se puede decidir fácilmente que el puntero ya no es excepcional, y cuando posteriormente emitir su entero a un puntero y tratar de eliminar la referencia, verás que el objeto se ha cosechado ya.

  6. 2

    Absolutamente no. Haciendo algunos hace una mala suposición de que el tamaño de un int y un puntero son los mismos. Casi siempre esto es ya el caso en plataformas de 64 bits. Si no son el mismo una pérdida de precisión y el final de puntero de valor será incorrecta.

    MyType* pValue = ...
    int stored = (int)pValue; //Just lost the upper 4 bytes on a 64 bit platform
    pValue = (MyType*)stored; //pValue is now invalid 
    pValue->SomeOp();  //Kaboom
  7. 2

    No, es no (siempre) seguro (por lo tanto no es seguro en general). Y es cubiertos por la norma.

    ISO C++ 2003, 5.2.10:

    1. Un puntero puede convertirse explícitamente a cualquier tipo integral lo suficientemente grande como para que se sostenga. La función de asignación de la implementación definido.
    2. Un valor de tipo integral o tipo de enumeración puede convertirse explícitamente a un puntero. Un puntero se convierte a un entero de tamaño suficiente (si tal existe en la aplicación) y de nuevo para el mismo tipo de puntero tendrá su valor original; las asignaciones entre los punteros y los enteros son la aplicación de lo contrario definido.

    (La de arriba énfasis son míos.)

    Por lo tanto, si usted sabe que los tamaños son compatibles, entonces la conversión es seguro.

    #include <iostream>
    
    //C++03 static_assert.
    #define ASSURE(cond) typedef int ASSURE[(cond) ? 1 : -1]
    
    //Assure that the sizes are compatible.
    ASSURE(sizeof (int) >= sizeof (char*));
    
    int main() {
        char c = 'A';
        char *p = &c;
        //If this program compiles, it is well formed.
        int i = reinterpret_cast<int>(p);
        p = reinterpret_cast<char*>(i);
        std::cout << *p << std::endl;
    }
  8. 0

    Uso uintptr_t de «stdint.h» o de «boost/stdint.h». Está garantizado para tener suficiente espacio de almacenamiento para un puntero.

    • Que va a ser <boost/cstdint.hpp>
  9. 0

    No es no. Incluso si descartamos la arquitectura problema, el tamaño de un puntero y un entero tiene sus diferencias. Un puntero puede ser de tres tipos en C++ : cerca, lejos, y enorme. Tienen diferentes tamaños. Y si hablamos de un entero su normalmente de 16 o 32 bits. Así casting entero en punteros y vice-verso no es seguro. El mayor cuidado tiene que ser tomado, como mucho las posibilidades de pérdida de precisión. En la mayoría de los casos un entero va a ser corto de espacio para almacenar un puntero, lo que resulta en la pérdida de valor.

  10. 0

    Si tu vas a estar haciendo cualquier sistema portátil de fundición, usted necesita usar algo como Microsoft INT_PTR/UINT_PTR, la seguridad después de que se basa en las plataformas de destino y lo que pretenden hacer a la INT_PTR. en general para la mayoría de los arithmatic char* o uint_8* funciona mejor mientras se typesafe(ish)

    • El _PTR en Microsoft/Intel mundo no siempre significa puntero. En el caso de que usted está citando, significa parámetro. De hecho, si se mira la referencia que se cita, ninguno de los _PTRs son punteros: son todos los parámetros.
    • son entero representaciones de los punteros, garantizado para mantener el ancho completo de un puntero. en la parte superior de que, PTR aquí significa puntero, si llegas a leer la descripción de INT_PTR, verás que: A signed integer type for pointer precision. Use when casting a pointer to an integer to perform pointer arithmetic.
    • Puedo estar equivocado, pero estoy convencido de que la documentación es errónea. Puede ser una broma, pero mira la definición de HALF_PTR. ¿Que sentido: se puede tener la mitad de un puntero? Ahora mira PINT_PTR o PHALF_PTR. ¿Por qué alguien tiene dos convenciones de nomenclatura de un solo tipo? La normal de MS convenio es tener un líder capital P o multicase Ptr (utilizado en .net). Si es un puntero a un puntero, entonces habría un PP prefijo o un _PTR_PTR sufijo (si _PTR realmente significa el puntero). Yo no esperaría que un P prefijo y un _PTR sufijo.
    • se utiliza para la alineación, es decir: si tengo una estructura que debe contener dos números pequeños y un puntero, y debe ser palabra alineados en cada plataforma, un puntero + 2 la mitad de los punteros permitirá que. el _PTR viene de la stdint convención (que también son números enteros, no de los punteros, por lo tanto P sería incorrecta en la SAL de estilo).
    • Acepto tu explicación, pero sigo siendo escéptico.
  11. 0

    A un int ? no siempre, si usted está en un equipo de 64 bits, a continuación, int es de sólo 4 bytes, sin embargo, los punteros son de 8 bytes, y así se terminaría con un puntero diferente cuando lanzas de nuevo de int.

    Sin embargo, hay maneras de conseguir alrededor de esto. Usted puede simplemente utilizar un byte de 8 tipo de datos long ,que iba a funcionar si usted está o no en 32/64 bits del sistema, tales como unsigned long long sin firmar porque no quiere firmar la extensión en sistemas de 32 bits.

    Es importante tener en cuenta que en Linux unsigned long siempre será el tamaño del puntero* así que si usted está apuntando a los sistemas Linux sólo podría utilizar eso.

    *De acuerdo a cppreference y probado también a mí mismo, pero no en todos los Linux y Linux como sistemas de

  12. 0

    Si el tema es que quiero hacer normal matemáticas en las que, probablemente, lo más seguro sería echarlo a un puntero a char (o mejor aún, * uint8_t), hacer tu tarea de matemáticas, y luego se echó atrás.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea