c++ - ¿Cuál es el comportamiento de una variable no inicializada utilizada como su propio inicializador?
initialization language-lawyer (3)
Acabo de notar que el siguiente código se puede compilar con clang / gcc / clang ++ / g ++, utilizando los
c11
c99
,
c11
,
c++11
.
int main(void) {
int i = i;
}
e incluso con
-Wall -Wextra
, ninguno de los compiladores reporta advertencias.
Al modificar el código a
int i = i + 1;
y con
-Wall
, pueden informar:
why.c:2:13: warning: variable ''i'' is uninitialized when used within its own initialization [-Wuninitialized]
int i = i + 1;
~ ^
1 warning generated.
Mis preguntas:
- ¿Por qué esto es permitido incluso por los compiladores?
- ¿Qué dicen los estándares C / C ++ sobre esto? Específicamente, ¿cuál es el comportamiento de esto? ¿UB o implementación dependiente?
Creo que está de acuerdo con recibir la advertencia en caso de
int i = i + 1;
como se esperaba, sin embargo, espera que se muestre la advertencia incluso en caso de
int i = i;
además.
¿Por qué esto es permitido incluso por los compiladores?
No hay nada inherentemente incorrecto con la declaración. Ver las discusiones relacionadas:
- ¿Por qué el compilador permite inicializar una variable consigo mismo?
- ¿Por qué es válida la inicialización de una nueva variable por sí misma?
para más información
¿Qué dicen los estándares C / C ++ sobre esto? Específicamente, ¿cuál es el comportamiento de esto? ¿UB o implementación dependiente?
Este es un comportamiento indefinido, ya que el tipo
int
puede tener representación de trampa y nunca ha tomado la dirección de la variable en discusión.
Entonces, técnicamente, enfrentará a UB tan pronto como intente usar el valor (indeterminado) almacenado en la variable
i
.
Debes activar las advertencias de tu compilador.
En
gcc
,
-
compile con
-Winit-self
para obtener una advertencia. Cía. -
Para C ++,
-Winit-self
está habilitado con-Wall
.
Debido a que no está inicializado cuando se usa para inicializarse, tiene un valor indeterminado en ese momento. Un valor indeterminado puede ser un valor no especificado o una representación trampa .
Si su implementación admite bits de relleno en tipos enteros y si el valor indeterminado en cuestión es una representación de trampa, su uso resulta en un comportamiento indefinido .
Si su implementación no tiene relleno en enteros, entonces el valor simplemente no está especificado y no hay un comportamiento indefinido.
EDITAR:
Para más detalles, el comportamiento aún puede ser indefinido si nunca se toma su dirección en algún momento. Esto se detalla en la sección 6.3.2.1p2 del estándar C11:
Si lvalue designa un objeto de duración de almacenamiento automático que podría haberse declarado con la clase de almacenamiento de registro (nunca se tomó su dirección), y ese objeto no se ha inicializado (no se ha declarado con un inicializador y no se ha realizado ninguna asignación antes de su uso) ), el comportamiento es indefinido.
Entonces, si nunca toma la dirección de
i
, entonces tiene un comportamiento indefinido.
De lo contrario, se aplican las declaraciones anteriores.
Esta es una advertencia, no está relacionada con el estándar.
Las advertencias son heurísticas con un enfoque "optimista".
La advertencia se emite solo cuando el compilador está
seguro de
que será un problema.
En casos como este, tiene mejor suerte con el
clang
o las versiones más recientes de
gcc
como se indica en los comentarios (vea otra pregunta mía relacionada:
¿por qué no recibo una advertencia de "usado sin inicializar" de gcc en este ejemplo trivial?
).
de todos modos, en el primer caso:
int i = i;
no hace nada, ya que
i==i
ya.
Es posible que la asignación esté completamente optimizada ya que es inútil.
Con compiladores que no "ven" la autoinicialización como un problema, puede hacerlo sin previo aviso:
int i = i;
printf("%d/n",i);
Mientras que esto desencadena una advertencia bien:
int i;
printf("%d/n",i);
Aún así, es lo suficientemente malo como para no ser advertido sobre esto, ya que a partir de ahora se
i
considera como
inicializado
.
En el segundo caso:
int i = i + 1;
Se debe realizar un cálculo entre un valor no inicializado y
1
.
El comportamiento indefinido sucede allí.