¿Cómo no es ilegal la inicialización del valor “int * ptr = int ()”?

86

El siguiente código (tomado de aquí ):

int* ptr = int();

compila en Visual C ++ y valor-inicializa el puntero.

¿Cómo es eso posible? Me refiero a que int()produce un objeto de tipo inty no puedo asignar un intpuntero.

¿Cómo es que el código anterior no es ilegal?

diente filoso
fuente
No es una respuesta, ¡pero una gran pregunta! Nunca había visto algo así.
Josh
6
Dado que las primitivas tienen un 'constructor' en C ++, int()produce el valor construido valor de int(que creo que es una cosa especificada en C ++ 03) y el valor predeterminado de intes 0. Esto es equivalente aint *ptr = 0;
sem.
7
@EmanuelEy: No, cualquier constante entera de valor cero puede usarse como una constante de puntero nulo, independientemente de cómo se implementen realmente los punteros.
Mike Seymour
1
@MooingDuck: No dije que NULLpudiera ser un valor distinto de cero. Dije que podría ser cualquier constante entera de valor cero (que incluye int()).
Mike Seymour
5
@DanielPryden Ese es un uso de la palabra "objeto" que no conocía anteriormente.
esponjoso

Respuestas:

110

int()es una expresión constante con un valor de 0, por lo que es una forma válida de producir una constante de puntero nulo. En última instancia, es solo una forma ligeramente diferente de decirint *ptr = NULL;

Jerry Coffin
fuente
3
+1, el bit de expresión constante es importante y falta en las 2 respuestas con votación positiva.
David Rodríguez - dribeas
¿Esto desaparece con C ++ 0x?
Neil G
@NeilG: Esto permanece igual en C ++ 11, aunque ahora también hay un nullptr, que puede usar en lugar de 0o NULLen el nuevo código.
Jerry Coffin
2
@Nils: claridad del código y declaración de su intención a través del código. Por supuesto, con C ++ 11, ahora desea usar nullptr porque también agrega el beneficio de verificaciones adicionales en tiempo de compilación a la mezcla.
Jamin Gray
3
@Nils porque obviamente 0podría significar una constante de puntero nulo o el número 0, mientras que nullptres obvio una constante de puntero nulo. Además, como dijo Jamin, también tiene "controles adicionales en tiempo de compilación". Intente pensar antes de escribir.
Miles Rout
35

Porque int()cede 0, que es intercambiable con NULL. NULLen sí se define como 0, a diferencia de C, NULLque es (void *) 0.

Tenga en cuenta que esto sería un error:

int* ptr = int(5);

y esto seguirá funcionando:

int* ptr = int(0);

0es un valor constante especial y, como tal, puede tratarse como un valor de puntero. Expresiones constantes que dan 0como resultado , 1 - 1como también se permiten como constantes de puntero nulo.

Blagovest Buyukliev
fuente
1
También tenga en cuenta que C's NULL tampoco es necesariamente (void *)0. Es simplemente una implementación definida "expresión constante entera con el valor 0, o una expresión convertida al tipo void *".
Jerry Coffin
@JerryCoffin Nunca he usado un compilador de C que se define NULLcomo (void*)0; siempre fue 0(o tal vez 0L). (Pero luego, cuando C90 se hizo (void*)0legal en C, ya estaba usando C ++.)
James Kanze
1
@JamesKanze: En ubuntu 11.04 y nuestra propia #if !defined(__cplusplus) \n #define NULL ((void*)0) \n #else \n #define NULL (0)versión de Linux, libio.h contiene: la versión actual de gcc en ubuntu es 4.5, en nuestro sistema es 4.0.
David Rodríguez - dribeas
5
" 0es un literal especial", solo porque es una expresión constante y tiene el valor especial 0. (1-1)es igualmente especial, también es una constante de puntero nulo, y también lo es int(). El hecho de 0ser literal es condición suficiente pero no necesaria para ser una expresión constante. Algo como strlen(""), aunque también tiene el valor especial 0, no es una expresión constante y, por lo tanto, no es una constante de puntero nulo.
Steve Jessop
@SteveJessop: Estoy de acuerdo con la corrección, realmente se trata del valor constante 0, no del 0literal.
Blagovest Buyukliev
18

La expresión se int()evalúa como un entero constante inicializado por defecto, que es el valor 0. Ese valor es especial: se utiliza para inicializar un puntero al estado NULL.

Mark Ransom
fuente
2
A esto le falta un detalle muy importante presente en la respuesta de Jerry: no es suficiente que la expresión dé el valor 0, sino que también debe ser una expresión constante . Para un contraejemplo, con int f() { return 0; }, la expresión f()produce el valor 0, pero no se puede usar para inicializar un puntero.
David Rodríguez - dribeas
@ DavidRodríguez-dribeas, en mi prisa por presentar una respuesta en los términos más simples posibles dejé esa parte fuera. Espero que sea aceptable ahora.
Mark Ransom
13

De n3290 (C ++ 03 usa texto similar), 4.10 Conversiones de puntero [conv.ptr] párrafo 1 (el énfasis es mío):

1 Una constante de puntero nulo es una expresión constante integral (5.19) prvalue de tipo entero que se evalúa como cero o un prvalue de tipo std :: nullptr_t. Una constante de puntero nulo se puede convertir en un tipo de puntero; el resultado es el valor de puntero nulo de ese tipo y se puede distinguir de cualquier otro valor de puntero de objeto o tipo de puntero de función. Esta conversión se denomina conversión de puntero nulo. [...]

int()es una expresión constante integral prvalue de tipo entero que se evalúa a cero (¡eso es un bocado!), y por lo tanto se puede usar para inicializar un tipo de puntero. Como puede ver, 0no es la única expresión integral con mayúsculas especiales.

Luc Danton
fuente
4

Bueno, int no es un objeto.

Creo que lo que está sucediendo aquí es que le estás diciendo al int * que apunte a alguna dirección de memoria determinada por int ()

así que si int () crea 0, int * apuntará a la dirección de memoria 0

Megatron
fuente
1
int()ciertamente es un objeto.
Lightness Races in Orbit
@Tomalak: No creo que lo sea. Es un tipo temporal de no clase, y creo que tengo razón al decir que estos no son objetos en lo que respecta al estándar C ++. Sin embargo, es un poco extraño, la sección sobre "objetos temporales" comienza hablando cuidadosamente sólo sobre temporales de tipo de clase, pero luego habla sobre referencias vinculantes y, por supuesto, puede vincular una referencia int(). Definir int i;, entonces sin duda, ies un objeto.
Steve Jessop
@Steve: Solo esperaría un debate sobre esto en el sentido de que los "objetos" son una región de almacenamiento en C ++, y los temporales realmente no tienen almacenamiento, ¿verdad? 1.8 / 1 no enumera explícitamente los temporales, pero parece que la intención está ahí para incluirlos.
Lightness Races in Orbit
1
@Tomalak: sí, de hecho, los temporales de tipo que no sea de clase no necesitan almacenamiento a menos que tome una referencia. Sin embargo, no importa, no importa mucho. La declaración "bueno, int no es un objeto" solo es verdadera porque intes un tipo, no un objeto. Ya sea que int()produzca un objeto o simplemente un rvalue, no afecta nada de lo que nadie haya dicho en otra parte.
Steve Jessop
@Steve: Eso es indiscutible :)
Lightness Races in Orbit