punteros - que es un puntero en c++
¿Por qué desreferenciar un puntero nulo es un comportamiento indefinido? (12)
Aquí hay una prueba simple y un ejemplo:
Asignar un puntero:
int * puntero;
? ¿Qué valor tiene el puntero cuando se crea?
? ¿A qué apunta el puntero?
? ¿Qué sucede cuando desreferencia este punto en su estado actual?
- Marcado al final de una lista enlazada. En una lista vinculada, un nodo apunta a otro nodo, excepto el último.
¿Cuál es el valor del puntero en el último nodo?
¿Qué sucede cuando cancela el campo "siguiente" del último nodo?
La necesidad de ser un valor que indique un puntero no apunta a nada o que está en un estado inválido. Aquí es donde entra en juego el concepto de puntero NULL. La lista enlazada puede usar un puntero NULL para indicar el final de la lista.
De acuerdo con ISO C ++, eliminar referencias de un puntero nulo es un comportamiento indefinido. Mi curiosidad es, ¿por qué? ¿Por qué el estándar ha decidido declararlo comportamiento indefinido? ¿Cuál es la razón de esta decisión? ¿Dependencia del compilador? No parece, porque según el estándar C99, hasta donde yo sé, está bien definido. Dependencia de la máquina? ¿Algunas ideas?
Aunque desreferenciar un puntero NULL en C / C ++ de hecho lleva un comportamiento indefinido desde el punto de vista del lenguaje, dicha operación está bien definida en los compiladores para los objetivos que tienen memoria en la dirección correspondiente. En este caso, el resultado de tal operación consiste en simplemente leer la memoria en la dirección 0.
Además, muchos compiladores le permitirán desreferenciar un puntero NULL siempre que no vincule el valor al que se hace referencia. Esto se hace para proporcionar compatibilidad con códigos no conformes pero generalizados, como
#define offsetof(st, m) ((size_t)(&((st *)0)->m))
Hubo incluso una discusión para hacer que este comportamiento sea parte del estándar.
De acuerdo con el estándar C original, NULL puede tener cualquier valor , no necesariamente cero .
La definición de idioma establece que para cada tipo de puntero, existe un valor especial, el `puntero nulo '', que se distingue de todos los demás valores de puntero y que'' se garantiza que no se puede comparar con un puntero a cualquier objeto o función ''. Es decir, un puntero nulo apunta definitivamente a ninguna parte; no es la dirección de ningún objeto o función
Hay un puntero nulo para cada tipo de puntero, y los valores internos de punteros nulos para diferentes tipos pueden ser diferentes.
(Desde http://c-faq.com/null/null1.html )
En realidad, puede desreferenciar un puntero nulo. Alguien lo hizo aquí: http://www.codeproject.com/KB/system/soviet_kernel_hack.aspx
La única forma de proporcionar un comportamiento definido sería agregar una verificación de tiempo de ejecución a cada referencia de referencia de puntero y a cada operación aritmética de puntero. En algunas situaciones, esta sobrecarga sería inaceptable y haría que C ++ no fuera adecuado para las aplicaciones de alto rendimiento para las que a menudo se utiliza.
C ++ le permite crear sus propios tipos de punteros inteligentes (o usar los que proporcionan las bibliotecas), que pueden incluir dicha comprobación en los casos en que la seguridad es más importante que el rendimiento.
La desreferenciación de un puntero nulo tampoco está definida en C, de acuerdo con la cláusula 6.5.3.2/4 del estándar C99.
La definición de un comportamiento consistente para eliminar la referenciación de un puntero NULL requeriría que el compilador verifique los punteros NULL antes de cada desreferencia en la mayoría de las arquitecturas de CPU. Esto es una impresión inaceptable para un lenguaje diseñado para la velocidad.
También solo soluciona una pequeña parte de un problema mayor: hay muchas maneras de tener un puntero no válido más allá de un puntero NULL.
La razón principal es que cuando escribieron el estándar C original, hubo una serie de implementaciones que lo permitieron, pero dieron resultados contradictorios.
En el PDP-11, sucedió que la dirección 0 siempre contenía el valor 0, por lo que desreferenciar un puntero nulo también daba el valor 0. Bastantes personas que usaron estas máquinas sintieron que, dado que eran la máquina original C, se escribieron en / usado para programar, que esto debería ser considerado un comportamiento canónico para C en todas las máquinas (aunque originalmente ocurrió de forma completamente accidental).
En algunas otras máquinas (me viene a la mente Interdata, aunque mi memoria podría estar equivocada) la dirección 0 se usó normalmente, por lo que podría contener otros valores. También había algún hardware en el que la dirección 0 era en realidad un hardware mapeado en memoria, por lo que leerlo / escribirlo hacía cosas especiales, para nada equivalentes a leer / escribir en absoluto la memoria normal.
Los campamentos no estarían de acuerdo con lo que debería suceder, por lo que adoptaron un comportamiento indefinido.
Editar: Supongo que debería agregar que para el momento en que escribió el estándar C ++, su comportamiento indefinido ya estaba bien establecido en C, y (al parecer) nadie pensó que había una buena razón para crear un conflicto en este punto, así que mantuvieron el mismo.
La verdadera pregunta es, ¿qué comportamiento esperarías?
Un puntero nulo es, por definición, un valor singular que representa la ausencia de un objeto. El resultado de desreferenciar un puntero es obtener una referencia al objeto apuntado.
Entonces, ¿cómo se obtiene una buena referencia ... de un puntero que apunta al vacío?
Tu no. Por lo tanto, el comportamiento indefinido .
Porque no puedes crear una referencia nula. C ++ no lo permite. Por lo tanto, no puede desreferenciar un puntero nulo.
Principalmente no está definido porque no hay una forma lógica de manejarlo.
Sospecho que es porque si el comportamiento está bien definido, el compilador tiene que insertar el código en cualquier lugar donde se eliminen referencias a los punteros. Si se define su implementación, entonces un posible comportamiento podría ser una falla importante. Si no se especifica, entonces los compiladores de algunos sistemas tienen una carga adicional excesiva o pueden generar código que causa bloqueos intensos.
Por lo tanto, para evitar cualquier posible carga adicional en los compiladores, dejaron el comportamiento indefinido.
This respuesta de @Johannes Schaub - litb, presenta una razón de ser interesante, que parece bastante convincente.
El problema formal con la mera desreferenciación de un puntero nulo es que la determinación de la identidad de la expresión lvalue resultante no es posible: Cada expresión que resulta de desreferenciar un puntero debe referirse inequívocamente a un objeto o una función cuando se evalúa esa expresión. Si desreferencia un puntero nulo, no tiene un objeto o función que identifique este lvalue. Este es el argumento que usa el estándar para prohibir referencias nulas.
Otro problema que se suma a la confusión es que la semántica del operador typeid
hace que parte de esta miseria esté bien definida. Dice que si se le dio un lvalue que resultó de quitarle la referencia a un puntero nulo, el resultado arroja una excepción bad_typeid
. Aunque, este es un área limitada donde existe una excepción (sin juego de palabras) al problema anterior de encontrar una identidad. Existen otros casos en los que se realiza una excepción similar a un comportamiento indefinido (aunque mucho menos sutil y con una referencia en las secciones afectadas).
El comité discutió para resolver este problema globalmente, al definir un tipo de lvalue que no tiene una identidad de objeto o función: el llamado valor l vacío. Ese concepto, sin embargo, todavía tenía problemas, y decidieron no adoptarlo .
Nota:
Marque esto como wiki de la comunidad, ya que la respuesta y el crédito deben ir al póster original. Solo estoy pegando las partes relevantes de la respuesta original aquí.
A veces necesita un puntero no válido (también vea MmBadPointer en Windows), para representar "nada".
Si todo fuera válido, entonces eso no sería posible. Por lo tanto, convirtieron a NULL
no válido y no le permitieron eliminarlo.