This article includes a list of general references, but it lacks sufficient corresponding inline citations. (March 2012) |
En programación informática , la comprobación de límites es cualquier método para detectar si una variable se encuentra dentro de ciertos límites antes de su uso. Por lo general, se utiliza para garantizar que un número se ajuste a un tipo determinado (comprobación de rango) o que una variable que se utiliza como índice de una matriz se encuentre dentro de los límites de la matriz (comprobación de índice). Una comprobación de límites fallida suele dar como resultado la generación de algún tipo de señal de excepción .
Dado que la comprobación de límites durante cada uso puede llevar mucho tiempo, no siempre se lleva a cabo. La eliminación de la comprobación de límites es una técnica de optimización del compilador que elimina la comprobación de límites innecesaria.
Una comprobación de rango es una comprobación para asegurarse de que un número se encuentra dentro de un cierto rango; por ejemplo, para garantizar que un valor que se va a asignar a un entero de 16 bits se encuentra dentro de la capacidad de un entero de 16 bits (es decir, comprobación de la capacidad de respuesta ). Esto no es exactamente lo mismo que la comprobación de tipos . [¿ Cómo? ] Otras comprobaciones de rango pueden ser más restrictivas; por ejemplo, una variable para almacenar el número de un mes del calendario puede declararse para aceptar solo el rango de 1 a 12.
La comprobación de índices significa que, en todas las expresiones que indexan una matriz, el valor del índice se compara con los límites de la matriz (que se establecieron cuando se definió la matriz) y, si el índice está fuera de los límites, la ejecución posterior se suspende debido a algún tipo de error. Debido a que leer o, especialmente, escribir un valor fuera de los límites de una matriz puede provocar que el programa funcione mal o se bloquee o habilite vulnerabilidades de seguridad (consulte desbordamiento de búfer ), la comprobación de índices es parte de muchos lenguajes de alto nivel .
Los primeros lenguajes de programación compilados con capacidad de verificación de índice incluyeron ALGOL 60 , ALGOL 68 y Pascal , así como lenguajes de programación interpretados como BASIC .
Muchos lenguajes de programación, como C , nunca realizan una comprobación automática de límites para aumentar la velocidad. Sin embargo, esto deja muchos errores de un dígito y desbordamientos de búfer sin detectar. Muchos programadores creen que estos lenguajes sacrifican demasiado por una ejecución rápida. [1] En su conferencia del Premio Turing de 1980 , CAR Hoare describió su experiencia en el diseño de ALGOL 60 , un lenguaje que incluía la comprobación de límites, diciendo:
Una consecuencia de este principio es que cada aparición de cada subíndice de cada variable subíndice se comprobaba en cada ocasión en tiempo de ejecución contra los límites superior e inferior declarados de la matriz. Muchos años después, preguntamos a nuestros clientes si querían que les ofreciéramos una opción para desactivar estas comprobaciones en beneficio de la eficiencia en las ejecuciones de producción. Unánimemente, nos instaron a que no lo hiciéramos; ya sabían con qué frecuencia se producen errores de subíndice en las ejecuciones de producción, donde no detectarlos podría ser desastroso. Observo con miedo y horror que incluso en 1980, los diseñadores y usuarios de lenguajes no han aprendido esta lección. En cualquier rama respetable de la ingeniería, el incumplimiento de estas precauciones elementales habría sido ilegal durante mucho tiempo.
Los lenguajes principales que aplican la comprobación en tiempo de ejecución incluyen Ada , C# , Haskell , Java , JavaScript , Lisp , PHP , Python , Ruby , Rust y Visual Basic . Los lenguajes D y OCaml tienen una comprobación de límites en tiempo de ejecución que se habilita o deshabilita con un interruptor del compilador. En C++, la comprobación en tiempo de ejecución no es parte del lenguaje, sino de la STL y se habilita con un interruptor del compilador (_GLIBCXX_DEBUG=1 o _LIBCPP_DEBUG=1). C# también admite regiones no seguras : secciones de código que (entre otras cosas) suspenden temporalmente la comprobación de límites para aumentar la eficiencia. Estas son útiles para acelerar pequeños cuellos de botella críticos en el tiempo sin sacrificar la seguridad de todo un programa.
El lenguaje de programación JS++ puede analizar si un índice de matriz o una clave de mapa está fuera de los límites en tiempo de compilación utilizando tipos existentes, que es un tipo nominal que describe si el índice o la clave está dentro o fuera de los límites y guía la generación de código. Se ha demostrado que los tipos existentes agregan solo 1 ms de sobrecarga a los tiempos de compilación. [2]
La seguridad añadida por la comprobación de límites necesariamente cuesta tiempo de CPU si la comprobación se realiza en software; sin embargo, si las comprobaciones se pudieran realizar mediante hardware, entonces la seguridad se puede proporcionar "gratis" sin costo de tiempo de ejecución. Un sistema temprano con comprobación de límites de hardware fue el mainframe de la serie ICL 2900 anunciado en 1974. [3] La computadora VAX tiene una instrucción de ensamblaje INDEX para la comprobación del índice de matriz que toma seis operandos, todos los cuales pueden usar cualquier modo de direccionamiento VAX. Las computadoras Burroughs B6500 y similares realizaban la comprobación de límites a través del hardware, independientemente de qué lenguaje de computadora se había compilado para producir el código de máquina. Un número limitado de CPU posteriores tienen instrucciones especializadas para comprobar límites, por ejemplo, la instrucción CHK2 en la serie Motorola 68000 .
Desde al menos 2005 se han llevado a cabo investigaciones sobre métodos para utilizar la unidad de gestión de memoria virtual integrada de x86 para garantizar la seguridad de los accesos a la matriz y al búfer. [4] En 2015, Intel proporcionó sus extensiones Intel MPX en su arquitectura de procesador Skylake , que almacena límites en un registro y una tabla de CPU en la memoria. A principios de 2017, al menos GCC admite extensiones MPX.