En informática , un puntero nulo (a veces abreviado como nullptr o null ) o referencia nula es un valor que indica que el puntero o la referencia no apunta a ningún objeto . Los programas suelen usar punteros nulos para representar condiciones como el final de una lista de longitud desconocida o el fallo al realizar alguna acción; este uso de punteros nulos se puede comparar con los tipos anulables y con el valor Nothing en un tipo de opción .
Un puntero nulo no debe confundirse con un puntero no inicializado : se garantiza que un puntero nulo se comparará de forma distinta a cualquier puntero que apunte a un objeto. Sin embargo, en general, la mayoría de los lenguajes no ofrecen tal garantía para los punteros no inicializados. Podría compararse como igual a otros punteros válidos, o podría compararse como igual a punteros nulos. Podría hacer ambas cosas en diferentes momentos, o la comparación podría ser un comportamiento indefinido . Además, en los lenguajes que ofrecen dicho soporte, el uso correcto depende de la experiencia individual de cada desarrollador y de las herramientas de análisis de código . Incluso cuando se usan correctamente, los punteros nulos son semánticamente incompletos , ya que no ofrecen la posibilidad de expresar la diferencia entre los valores "no aplicable", "actualmente desconocido" y "aún no determinado". [ 1 ]
En sistemas con una arquitectura etiquetada , un puntero posiblemente nulo puede reemplazarse por una unión etiquetada que impone un manejo explícito del caso excepcional; de hecho, un puntero posiblemente nulo puede verse como un puntero etiquetado con una etiqueta calculada.
Dado que un puntero nulo no apunta a un objeto significativo, intentar acceder a los datos almacenados en esa ubicación de memoria (inválida) puede provocar un error en tiempo de ejecución o el bloqueo inmediato del programa. Este es el error de puntero nulo o excepción de puntero nulo . Es uno de los tipos más comunes de vulnerabilidades de software, [ 2 ] y Tony Hoare , quien introdujo el concepto, lo ha denominado un "error de mil millones de dólares". [ 3 ]
do
En C , se garantiza que dos punteros nulos de cualquier tipo se compararán como iguales. [ 4 ]NULL Se proporciona la macro del preprocesador , definida como una constante de puntero nulo definida por la implementación en , [ 5 ] que en C99 se puede expresar de forma portable con , el valor entero convertido al tipo (véase puntero al tipo void ). [ 6 ] Desde C23 , un puntero nulo se representa con que es de tipo (introducido por primera vez en C++11), proporcionando un puntero nulo con seguridad de tipos. [ 7 ]<stdlib.h>#define NULL ((void*)0)0void*nullptrnullptr_t
El estándar C no establece que el puntero nulo sea lo mismo que el puntero a la dirección de memoria 0, aunque en la práctica podría ser así. Desreferenciar un puntero nulo es un comportamiento indefinido en C, [ 8 ] y una implementación conforme puede asumir que cualquier puntero desreferenciado no es nulo.
En la práctica, la desreferenciación de un puntero nulo puede resultar en un intento de lectura o escritura en memoria no mapeada, lo que desencadena un fallo de segmentación o una violación de acceso a memoria. Esto puede manifestarse como un bloqueo del programa o transformarse en una excepción de software que puede ser capturada por el código del programa. Sin embargo, existen ciertas circunstancias en las que esto no sucede. Por ejemplo, en el modo real x86 , la dirección es legible y generalmente también escribible, y la desreferenciación de un puntero a esa dirección es una acción perfectamente válida pero normalmente no deseada que puede conducir a un comportamiento indefinido pero que no provoca un bloqueo en la aplicación; si un puntero nulo se representa como un puntero a esa dirección, su desreferenciación conducirá a ese comportamiento. Hay ocasiones en las que la desreferenciación de un puntero a la dirección cero es intencional y está bien definida; por ejemplo, el código BIOS escrito en C para dispositivos x86 de 16 bits en modo real puede escribir la tabla de descriptores de interrupción (IDT) en la dirección física 0 de la máquina desreferenciando un puntero con el mismo valor que un puntero nulo para escritura. También es posible que el compilador optimice la desreferenciación del puntero nulo, evitando un fallo de segmentación pero causando otro comportamiento no deseado. [ 9 ]0000:0000
C++
En C++ , si bien la NULLmacro se heredó de C, el literal entero para cero se ha preferido tradicionalmente para representar una constante de puntero nulo. [ 10 ] Sin embargo, C++11 introdujo la constante de puntero nulo explícita nullptry el tipo nullptr_tpara ser utilizado en su lugar, proporcionando un puntero nulo seguro en cuanto a tipos. nullptry el tipo nullptr_tse introdujeron posteriormente en C en C23.
Otros idiomas
Los lenguajes de programación utilizan diferentes literales para el puntero nulo . En Java y C# , el literal nullse proporciona como un literal para tipos de referencia. En Pascal y Swift , un puntero nulo se llama nil. En Eiffel , se llama voidreferencia. En Rust , la ausencia de un valor se denota como None, pero un verdadero puntero nulo es std::ptr::null().
Desreferenciación nula
Dado que un puntero nulo no apunta a un objeto significativo, un intento de desreferenciar (es decir, acceder a los datos almacenados en esa ubicación de memoria) un puntero nulo generalmente (aunque no siempre) provoca un error en tiempo de ejecución o un fallo inmediato del programa. MITRE enumera el error de puntero nulo como una de las vulnerabilidades de software más explotadas. [ 11 ]
- En C , la desreferenciación de un puntero nulo es un comportamiento indefinido . [ 8 ] Muchas implementaciones hacen que dicho código provoque que el programa se detenga con una violación de acceso , porque la representación del puntero nulo se elige como una dirección que el sistema nunca asigna para almacenar objetos. Sin embargo, este comportamiento no es universal. Tampoco está garantizado, ya que los compiladores pueden optimizar los programas bajo el supuesto de que no presentan comportamiento indefinido. Este comportamiento es el mismo en C++ , ya que no existe una excepción de puntero nulo en el lenguaje C++. En plataformas como sistemas tipo Unix y Windows con el compilador Visual Studio , una violación de acceso provoca
SIGSEGVque se emita una señal de C/C++. Aunque en C/C++ las desreferenciaciones nulas no son excepciones que se puedan capturar en bloquestry/ de C++catch, es posible "capturar" dicha violación de acceso usando (std::)signal()en C/C++ para especificar un manejador que se llamará cuando se emita esa señal.- Algunas bibliotecas externas de C++, como las bibliotecas POCO C++ , incluyen una
NullPointerExceptionclase. A diferencia de Java, dondejava.lang.NullPointerExceptionextiendejava.lang.RuntimeException,Poco::NullPointerExceptionen su lugar extiendePoco::LogicException. [ 12 ]
- Algunas bibliotecas externas de C++, como las bibliotecas POCO C++ , incluyen una
- En Cyclone , una comprobación de puntero nulo fallida generará una excepción
Null_Exception. - En D , al igual que en C++, la desreferenciación de un puntero nulo produce un fallo de segmentación.
- En Delphi y muchas otras implementaciones de Pascal, la constante
nilrepresenta un puntero nulo a la primera dirección de memoria, que también se utiliza para inicializar variables gestionadas. Al desreferenciarla, se genera una excepción externa del sistema operativo que se asigna a una instancia de excepción de PascalEAccessViolationsi laSystem.SysUtilsunidad está enlazada en lausescláusula. - En Java , el acceso a una referencia nula (
null) provoca unaNullPointerException(NPE), que puede ser capturada por el código de manejo de errores, pero la práctica preferida es garantizar que tales excepciones nunca ocurran. - En .NET y C# , el acceso a una referencia nula (
null) provocaSystem.NullReferenceExceptionque se lance una excepción. Aunque capturar estas excepciones generalmente se considera una mala práctica, este tipo de excepción puede ser capturada y manejada por el programa. - En Objective-C , se pueden enviar mensajes a un
nilobjeto (que es un puntero nulo) sin que el programa se interrumpa; el mensaje simplemente se ignora y el valor de retorno (si lo hay) esnilo0, dependiendo del tipo. [ 13 ] - En Rust , desreferenciar un puntero nulo (
std::ptr::null()) en ununsafebloque produce un comportamiento indefinido, que generalmente resulta en un fallo de segmentación o memoria corrupta. - Antes de la introducción de la Prevención de Acceso en Modo Supervisor (SMAP), se podía explotar un error de desreferenciación de puntero nulo mapeando la página cero en el espacio de direcciones del atacante y, por lo tanto, haciendo que el puntero nulo apuntara a esa región. Esto podía conducir a la ejecución de código en algunos casos. [ 14 ]
Mitigación
Aunque podría haber lenguajes sin valores nulos, la mayoría sí tienen la posibilidad de que existan, por lo que hay técnicas para evitar o facilitar la depuración de desreferencias de punteros nulos. [ 15 ] Bond et al. sugieren modificar la máquina virtual de Java (JVM) para que realice un seguimiento de la propagación de valores nulos. [ 15 ]
Existen tres niveles de manejo de referencias nulas, en orden de efectividad:
- idiomas sin nulo;
- lenguajes que pueden analizar estáticamente el código para evitar la posibilidad de desreferenciación nula en tiempo de ejecución;
- Si la desreferenciación nula puede ocurrir en tiempo de ejecución, existen herramientas que ayudan a la depuración.
Los lenguajes puramente funcionales son un ejemplo de nivel 1, ya que no proporcionan acceso directo a punteros y todo el código y los datos son inmutables. El código de usuario que se ejecuta en lenguajes interpretados o de máquina virtual generalmente no sufre el problema de la desreferenciación de punteros nulos.
Cuando un lenguaje proporciona o utiliza punteros que podrían volverse nulos, es posible evitar las desreferencias nulas en tiempo de ejecución proporcionando comprobaciones en tiempo de compilación a través de análisis estático u otras técnicas, con asistencia sintáctica de características del lenguaje como las que se ven en el lenguaje de programación Eiffel con seguridad Void [ 16 ] para evitar desreferencias nulas, D , [ 17 ] y Rust . [ 18 ] Los sistemas de tipos nulos también se han adoptado ampliamente en lenguajes de programación convencionales: Kotlin , Swift y TypeScript los incluyen desde el principio, mientras que C# y Scala se han adaptado posteriormente con sistemas de tipos nulos. [ 19 ]
En algunos lenguajes, el análisis puede realizarse utilizando herramientas externas, pero estas son débiles en comparación con el soporte directo del lenguaje mediante comprobaciones del compilador, ya que están limitadas por la propia definición del lenguaje.
El último recurso del nivel 3 se da cuando se produce una referencia nula en tiempo de ejecución; las herramientas de depuración pueden ser de ayuda.
Historia
En 2009, Tony Hoare declaró [ 3 ] [ 20 ] [ 21 ] que inventó la referencia nula en 1965 como parte del lenguaje ALGOL W. En esa referencia de 2009, Hoare describe su invención como un "error de mil millones de dólares":
Lo llamo mi error de mil millones de dólares. Fue la invención de la referencia nula en 1965. En aquel entonces, estaba diseñando el primer sistema de tipos completo para referencias en un lenguaje orientado a objetos ( ALGOL W ). Mi objetivo era garantizar que todo uso de referencias fuera absolutamente seguro, con comprobaciones automáticas realizadas por el compilador. Pero no pude resistir la tentación de incluir una referencia nula, simplemente porque era muy fácil de implementar. Esto ha provocado innumerables errores, vulnerabilidades y fallos del sistema, que probablemente han causado pérdidas y daños por valor de mil millones de dólares en los últimos cuarenta años.
Véase también
Notas
- ↑ Toussaint, Etienne; Guagliardo, Paolo; Libkin, Leonid; Sequeda, Juan (2022). "Problemas con los valores nulos, opiniones de los usuarios". Actas de la Fundación VLDB . 15 : 2613–2625 . doi : 10.14778/3551793.3551818 . hdl : 20.500.11820/29fe40ac-24be-4b44-a67f-5c191e97092a .
- ↑ "CWE-476: Desreferenciación de puntero nulo" . MITRE .
- 1 2 Tony Hoare (25 de agosto de 2009). "Referencias nulas: El error de mil millones de dólares" . InfoQ.com.
- ↑ ISO/IEC 9899 , cláusula 6.3.2.3, párrafo 4.
- ↑ ISO/IEC 9899 , cláusula 7.17, párrafo 3: NULL... que se expande a una constante de puntero nulo definida por la implementación...
- ↑ ISO/IEC 9899 , cláusula 6.3.2.3, párrafo 3.
- ↑ "WR14-N3042: Introducir la constante nullptr" . open-std.org . 22 de julio de 2022. Archivado del original el 24 de diciembre de 2022.
- 1 2 ISO/IEC 9899 , cláusula 6.5.3.2, párrafo 4, esp. nota al pie 87.
- ↑ Lattner, Chris (13 de mayo de 2011). "Lo que todo programador de C debería saber sobre el comportamiento indefinido #1/3" . blog.llvm.org . Archivado del original el 14 de junio de 2023. Consultado el 14 de junio de 2023 .
- ↑ Stroustrup, Bjarne (marzo de 2001). «Capítulo 5: El calificador (§5.4) evita la redefinición accidental de y garantiza que se pueda usar donde se requiere una constante». El lenguaje de programación C++ (14.ª reimpresión de la 3.ª ed.). Estados Unidos y Canadá: Addison–Wesley. pág . 88. ISBN
constNULLNULL0-201-88954-4. - ↑ "CWE-476: Desreferenciación de puntero nulo" . MITRE .
- ↑ "Clase Poco::NullPointerException" . docs.pocoproject.org . Consultado el 17 de agosto de 2025 .
- ↑ "El lenguaje de programación Objective-C 2.0" . Sección "Envío de mensajes a nil".
- ↑ "Desreferenciación de puntero NULL del kernel de OS X explotable en AppleGraphicsDeviceControl" .
- 1 2 Bond, Michael D.; Nethercote, Nicholas; Kent, Stephen W.; Guyer, Samuel Z.; McKinley, Kathryn S. (2007). "Rastreando manzanas podridas". Actas de la 22.ª conferencia anual ACM SIGPLAN sobre sistemas y aplicaciones de programación orientada a objetos - OOPSLA '07 . pág. 405. doi : 10.1145/1297027.1297057 . ISBN 9781595937865. S2CID 2832749 .
- ↑ "Seguridad ante vacíos: Antecedentes, definición y herramientas" . Consultado el 24 de noviembre de 2021 .
- ↑ Bartosz Milewski. "SafeD – Lenguaje de programación D" . Consultado el 17 de julio de 2014 .
- ↑ "Seguridad sin miedo: protección de la memoria" . Archivado del original el 8 de noviembre de 2020. Consultado el 4 de noviembre de 2020 .
- ↑ Madsen, Magnus; Pol, Jaco van de (2021). "Tipos relacionales anulables con unificación booleana". Actas de la ACM sobre lenguajes de programación . 5 : 1–28 . doi : 10.1145/3485487 .
- ↑ Tony Hoare (25 de agosto de 2009). "Presentación: "Referencias nulas: El error de mil millones de dólares"" . InfoQ.com.
- ↑ Contieri, Maxi (18-06-2025). "Code Smell 304 - Null Pointer Exception" . Maximiliano Contieri - Software Design . Recuperado el 18-06-2025 .
Referencias
- Comité Técnico Conjunto ISO/IEC JTC 1, Subcomité SC 22, Grupo de Trabajo WG 14 (08/09/2007). Norma Internacional ISO/IEC 9899 (PDF) (Borrador del Comité).
{{cite book}}: CS1 maint: nombres múltiples: lista de autores ( enlace ) CS1 maint: nombres numéricos: lista de autores ( enlace )
- Punteros (programación informática)