¿Dónde dice exactamente el estándar C++ que desreferenciar un puntero no inicializado es un comportamiento indefinido?
Hasta ahora no encuentro cómo deducir lo siguiente:
int* ptr;
*ptr = 0;
es un comportamiento indefinido.
En primer lugar, está 5.3.1/1 que indica que *
significa indirección que se convierte T*
a T
. Pero esto no dice nada sobre la UB.
Luego, a menudo se cita 3.7.3.2/4 que dice que el uso de la función de desasignación en un puntero no nulo invalida el puntero y el uso posterior del puntero no válido es UB. Pero en el código anterior no hay nada sobre la desasignación.
¿Cómo se puede deducir UB en el código anterior?
La sección 4.1 parece candidata ( el énfasis es mío ):
Un valor l (3.10) de un tipo T que no es función ni matriz se puede convertir en un valor r. Si T es un tipo incompleto, un programa que requiere esta conversión está mal formado. Si el objeto al que se refiere el valor l no es un objeto de tipo T y no es un objeto de un tipo derivado de T, o si el objeto no está inicializado , un programa que requiere esta conversión tiene un comportamiento indefinido . Si T es un tipo que no es de clase, el tipo de rvalue es la versión cv no calificada de T. De lo contrario, el tipo de rvalue es T.
Estoy seguro de que con solo buscar "no inicial" en la especificación se pueden encontrar más candidatos.
Encontré que la respuesta a esta pregunta es un rincón inesperado del borrador del estándar C++ , sección 24.2
Requisitos del iterador , específicamente la sección 24.2.1
En general, párrafos 5 y 10 que dicen respectivamente ( énfasis mío ):
[...][ Ejemplo: Después de la declaración de un puntero x no inicializado (como con int* x;), siempre se debe suponer que x tiene un valor singular de puntero . —fin del ejemplo ] [...] Los valores desreferenciables siempre son no singulares.
y:
Un iterador no válido es un iterador que puede ser singular. 268
y la nota al pie 268
dice:
Esta definición se aplica a los punteros, ya que los punteros son iteradores . El efecto de eliminar la referencia a un iterador que ha sido invalidado no está definido.
Aunque parece que existe cierta controversia sobre si un puntero nulo es singular o no , parece que el término valor singular debe definirse adecuadamente de una manera más general.
La intención del singular parece estar bien resumida en el informe de defectos 278. ¿Qué significa validez del iterador? bajo la sección de justificación que dice:
¿Por qué decimos "puede ser singular" , en lugar de "es singular"? Esto se debe a que un iterador válido es aquel que se sabe que no es singular . Invalidar un iterador significa cambiarlo de tal manera que ya no se sepa que no es singular. Un ejemplo: se dice correctamente que insertar un elemento en el medio de un vector invalida todos los iteradores que apuntan al vector. Eso no significa necesariamente que todos se vuelvan singulares .
Entonces, la invalidación y la no inicialización may
crean un valor que es singular , pero como no podemos probar que no sean singulares , debemos asumir que son singulares .
Actualizar
Un enfoque alternativo de sentido común sería tener en cuenta que el borrador de la sección estándar 5.3.1
Operadores unarios, párrafo 1 , que dice ( el énfasis es mío ):
El operador unario * realiza direccionamiento indirecto: la expresión a la que se aplica será un puntero a un tipo de objeto, o un puntero a un tipo de función y el resultado es un valor l que se refiere al objeto o función al que apunta la expresión.[. ..]
y si luego vamos a la sección 3.10
Lvalues y rvalues, el párrafo 1 dice ( el énfasis es mío ):
Un valor-l (llamado así históricamente porque los valores-l podían aparecer en el lado izquierdo de una expresión de asignación) designa una función o un objeto. [...]
pero ptr
no señalará, excepto por casualidad, un objeto válido .
La pregunta del OP es una tontería. No existe ningún requisito de que el Estándar diga que ciertos comportamientos no están definidos y, de hecho, yo diría que toda esa redacción debe eliminarse del Estándar porque confunde a las personas y hace que el Estándar sea más detallado de lo necesario.
La Norma define ciertos comportamientos. La pregunta es, ¿especifica algún comportamiento en este caso? Si no es así, el comportamiento no está definido, lo diga explícitamente o no.
De hecho, la especificación de que algunas cosas no están definidas se deja en el Estándar principalmente como una ayuda de depuración para los redactores de los Estándares, siendo la idea generar una contradicción si hay un requisito en un lugar que entra en conflicto con una declaración explícita de comportamiento indefinido en otro. : esa es una forma de probar un defecto en el Estándar. Sin la declaración explícita de comportamiento indefinido, la otra cláusula que prescribe el comportamiento sería normativa e indiscutible.
La evaluación de un puntero no inicializado provoca un comportamiento indefinido. Dado que para desreferenciar el puntero primero es necesario evaluarlo, esto implica que la desreferenciación también provoca un comportamiento indefinido.
Esto fue cierto tanto en C++11 como en C++14, aunque la redacción cambió.
En C++14 está completamente cubierto por [dcl.init]/12:
Cuando se obtiene el almacenamiento de un objeto con duración de almacenamiento automático o dinámico, el objeto tiene un valor indeterminado y, si no se realiza ninguna inicialización para el objeto, ese objeto conserva un valor indeterminado hasta que se reemplaza ese valor.
Si una evaluación produce un valor indeterminado, el comportamiento no está definido excepto en los siguientes casos:
donde los "siguientes casos" son operaciones particulares en unsigned char
.
En C++11, [conv.lval/2] cubrió esto bajo el procedimiento de conversión de valor a valor (es decir, recuperar el valor del puntero del área de almacenamiento indicada por ptr
):
Un glvalue de un tipo T que no es función ni matriz se puede convertir en un prvalue. Si T es un tipo incompleto, un programa que requiere esta conversión está mal formado. Si el objeto al que se refiere glvalue no es un objeto de tipo T y no es un objeto de un tipo derivado de T, o si el objeto no está inicializado, un programa que requiere esta conversión tiene un comportamiento indefinido.
La parte en negrita se eliminó para C++14 y se reemplazó con el texto adicional en [dcl.init/12].