En informática (en particular, en programación ), un valor indefinido es una condición en la que una expresión no tiene un valor correcto , aunque sea sintácticamente correcto. Un valor indefinido no debe confundirse con una cadena vacía , un valor booleano "falso" u otros valores "vacíos" (pero definidos). Según las circunstancias, la evaluación de un valor indefinido puede dar lugar a una excepción o a un comportamiento indefinido , pero en algunos lenguajes de programación los valores indefinidos pueden aparecer durante un curso normal y predecible de ejecución del programa .
Los lenguajes de tipado dinámico suelen tratar los valores indefinidos de forma explícita cuando es posible. Por ejemplo, Perl tiene undef
el operador [1] que puede "asignar" dicho valor a una variable. En otros sistemas de tipos, un valor indefinido puede significar un valor desconocido e impredecible, o simplemente un fallo del programa al intentar evaluarlo. Los tipos que aceptan valores nulos ofrecen un enfoque intermedio; consulte a continuación.
El valor de una función parcial no está definido cuando su argumento está fuera de su dominio de definición . Esto incluye numerosos casos aritméticos como la división por cero , la raíz cuadrada o el logaritmo de un número negativo , etc. Otro ejemplo común es el acceso a una matriz con un índice que está fuera de los límites, como lo está el valor en una matriz asociativa para una clave que no contiene. Existen varias formas de manejar estas situaciones en la práctica:
En aplicaciones en las que los valores indefinidos deben manejarse con elegancia, es habitual reservar un valor nulo especial que se distinga de los valores normales. Esto resuelve la dificultad creando un valor definido para representar el caso que antes no estaba definido. Hay muchos ejemplos de esto:
EOF
para indicar que no hay más entradas disponibles. La getchar()
función devuelve el siguiente carácter de entrada disponible o EOF
si no hay más disponibles. (El código de caracteres ASCII define un carácter nulo para este propósito, pero la biblioteca de E/S estándar desea poder enviar y recibir caracteres nulos, por lo que define un EOF
valor separado).NULL
para indicar datos faltantes.defined()
predicado. [2]Si bien los lenguajes tipados dinámicamente a menudo garantizan que las variables no inicializadas tengan como valor predeterminado un valor nulo, los valores tipados estáticamente a menudo no lo hacen y distinguen los valores nulos (que están bien definidos) de los valores no inicializados (que no lo están). [3]
Algunos lenguajes de programación tienen un concepto de manejo de excepciones para lidiar con la falla en el retorno de un valor. La función retorna de una manera definida, pero no retorna un valor, por lo que no hay necesidad de inventar un valor especial para retornarlo.
Una variación de esto es el manejo de señales , que se realiza a nivel del sistema operativo y no está integrado en un lenguaje de programación. Los manejadores de señales pueden intentar algunas formas de recuperación, como terminar parte de un cálculo, pero sin tanta flexibilidad como el manejo de excepciones completamente integrado.
Una función que nunca retorna tiene un valor indefinido porque el valor nunca se puede observar. A estas funciones se les asigna formalmente el tipo bottom , que no tiene valores. Los ejemplos se dividen en dos categorías:
exit
llamada al sistema . Desde dentro del programa, esto es indistinguible del caso anterior, pero marca una diferencia para quien invoca el programa.Todos los métodos anteriores para manejar valores indefinidos requieren que se detecte la indefinición. Es decir, la función llamada determina que no puede devolver un resultado normal y toma alguna acción para notificar al llamador. En el otro extremo del espectro, el comportamiento indefinido hace que el llamador tenga la responsabilidad de evitar llamar a una función con argumentos fuera de su dominio. No hay límite sobre lo que podría suceder. En el mejor de los casos, un fallo fácilmente detectable ; en el peor, un error sutil en un cálculo aparentemente no relacionado.
(La definición formal de "comportamiento indefinido" incluye posibilidades aún más extremas, como cosas como " detenerse y prenderse fuego " y "hacer que los demonios salgan volando de tu nariz". [4] )
El ejemplo clásico es una referencia de puntero colgante . Es muy rápido desreferenciar un puntero válido , pero puede ser muy complejo determinar si un puntero es válido. Por lo tanto, el hardware de la computadora y los lenguajes de bajo nivel como C no intentan validar los punteros antes de desreferenciarlos, sino que pasan la responsabilidad al programador. Esto ofrece velocidad a expensas de la seguridad.
La definición estricta de un valor indefinido es una salida válida superficialmente (no nula) que no tiene sentido pero que no desencadena un comportamiento indefinido. Por ejemplo, pasar un número negativo a la función de raíz cuadrada inversa rápida producirá un número. No es un número muy útil, pero el cálculo se completará y devolverá algo .
Los valores indefinidos se producen con mucha frecuencia en hardware. Si un cable no transporta información útil, sigue existiendo y tiene un cierto nivel de tensión. La tensión no debe ser anormal (por ejemplo, no debe ser una sobretensión perjudicial), pero el nivel lógico en particular no es importante.
La misma situación ocurre en el software cuando se proporciona un búfer de datosstrftime
pero no se llena por completo. Por ejemplo, la función de la biblioteca C convierte una marca de tiempo a un formato legible para humanos en un búfer de salida proporcionado. Si el búfer de salida no es lo suficientemente grande para contener el resultado, se devuelve un error y el contenido del búfer no está definido.
En la otra dirección, la open
llamada al sistema en POSIX toma tres argumentos: un nombre de archivo, algunos indicadores y un modo de archivo. El modo de archivo solo se utiliza si los indicadores incluyen O_CREAT
. Es común utilizar una forma de dos argumentos de open
, que proporciona un valor indefinido para el modo de archivo, cuando O_CREAT
se omite .
A veces resulta útil trabajar con estos valores indefinidos de forma limitada. El cálculo general puede seguir estando bien definido si luego se ignora el valor indefinido.
Como ejemplo de esto, el lenguaje C permite convertir un puntero en un entero, aunque el valor numérico de ese entero no esté definido. Aún puede ser útil para depurar, para comparar dos punteros para determinar su igualdad o para crear una lista enlazada XOR .
El manejo seguro de valores indefinidos es importante en los sistemas de control de concurrencia optimistas , que detectan las condiciones de carrera después de que se han producido. Por ejemplo, leer una variable compartida protegida por seqlock producirá un valor indefinido antes de determinar que se ha producido una condición de carrera. A continuación, descartará los datos indefinidos y volverá a intentar la operación. Esto produce un resultado definido siempre que las operaciones realizadas sobre los valores indefinidos no produzcan un comportamiento indefinido completo.
Otros ejemplos de valores indefinidos que pueden resultar útiles son los generadores de números aleatorios y las funciones hash . Los valores específicos que se devuelven no están definidos, pero tienen propiedades bien definidas y pueden utilizarse sin errores.
En la teoría de la computabilidad , la indefinición de una expresión se denota como expr ↑, y su definición como expr ↓.