En programación informática , una referencia es un valor que permite a un programa acceder indirectamente a un dato específico , como el valor de una variable o un registro , en la memoria del ordenador o en algún otro dispositivo de almacenamiento . Se dice que la referencia alude al dato, y acceder a este se denomina desreferenciar la referencia. Una referencia es distinta del dato en sí.
Una referencia es un tipo de dato abstracto y puede implementarse de diversas maneras. Normalmente, una referencia alude a datos almacenados en la memoria de un sistema determinado, y su valor interno es la dirección de memoria de los datos; es decir, una referencia se implementa como un puntero . Por este motivo, se suele decir que una referencia "apunta" a los datos. Otras implementaciones incluyen un desplazamiento (diferencia) entre la dirección del dato y una dirección "base" fija, un índice o identificador utilizado en una operación de búsqueda en una matriz o tabla , un identificador del sistema operativo , una dirección física en un dispositivo de almacenamiento o una dirección de red, como una URL .
Representación formal
Una referencia R es un valor que admite una operación, dereference( R ), que produce un valor. Normalmente, la referencia está tipificada de forma que devuelve valores de un tipo específico, por ejemplo: [ 1 ] [ 2 ].
Interfaz Referencia < T > { T valor (); }A menudo, la referencia también admite una operación de asignación store( R , x ), lo que significa que es una variable abstracta . [ 1 ]
Usar
Las referencias se utilizan ampliamente en programación , especialmente para pasar de forma eficiente datos grandes o mutables como argumentos a procedimientos , o para compartirlos entre diferentes usos. En particular, una referencia puede apuntar a una variable o registro que contiene referencias a otros datos. Esta idea es la base del direccionamiento indirecto y de muchas estructuras de datos enlazadas , como las listas enlazadas . Las referencias aumentan la flexibilidad en cuanto a dónde se pueden almacenar los objetos, cómo se asignan y cómo se pasan entre áreas de código . Siempre que se pueda acceder a una referencia a los datos, se puede acceder a ellos a través de ella, y no es necesario mover los datos en sí. También facilitan el intercambio de datos entre diferentes áreas de código; cada una mantiene una referencia a ellos.
Las referencias pueden generar una complejidad significativa en un programa, en parte debido a la posibilidad de referencias colgantes y no válidas , y en parte porque la topología de los datos con referencias es un grafo dirigido , cuyo análisis puede resultar bastante complicado. No obstante, las referencias siguen siendo más fáciles de analizar que los punteros debido a la ausencia de aritmética de punteros .
El mecanismo de referencias, si bien varía en su implementación, es una característica fundamental de los lenguajes de programación, común a casi todos los lenguajes modernos. Incluso algunos lenguajes que no admiten el uso directo de referencias, sí lo hacen de forma interna o implícita. Por ejemplo, la convención de llamada por referencia puede implementarse mediante el uso explícito o implícito de referencias.
Ejemplos
Los punteros son el tipo de referencia más primitivo. Debido a su estrecha relación con el hardware subyacente, son uno de los tipos de referencia más potentes y eficientes. Sin embargo, precisamente por esta relación, los punteros requieren que el programador comprenda a fondo los detalles de la arquitectura de memoria. Dado que los punteros almacenan la dirección de una ubicación de memoria, en lugar de un valor directamente, un uso inadecuado de los punteros puede provocar un comportamiento indefinido en un programa, especialmente debido a punteros colgantes o punteros salvajes . Los punteros inteligentes son estructuras de datos opacas que se comportan como punteros, pero solo se puede acceder a ellos mediante métodos específicos.
Un identificador es una referencia abstracta y puede representarse de diversas maneras. Un ejemplo común son los identificadores de archivo (la estructura de datos FILE en la biblioteca de E/S estándar de C ), que se utilizan para abstraer el contenido de un archivo. Generalmente, representa tanto el archivo en sí, como al solicitar un bloqueo sobre él, como una posición específica dentro de su contenido, como al leerlo.
En la computación distribuida , la referencia puede contener más que una dirección o un identificador; también puede incluir una especificación integrada de los protocolos de red utilizados para localizar y acceder al objeto referenciado, así como la forma en que se codifica o serializa la información. Por ejemplo, una descripción WSDL de un servicio web remoto puede considerarse una forma de referencia, ya que incluye una especificación completa de cómo localizar y conectarse a un servicio web específico . Otro ejemplo es una referencia a un objeto distribuido en tiempo real : se trata de una especificación completa de cómo construir un pequeño componente de software llamado proxy , que posteriormente participará en una interacción punto a punto y mediante la cual la máquina local puede acceder a datos replicados o que existen únicamente como un flujo de mensajes débilmente consistente. En todos estos casos, la referencia incluye el conjunto completo de instrucciones, o una receta, para acceder a los datos; en este sentido, cumple la misma función que un identificador o una dirección en memoria.
Si tenemos un conjunto de claves K y un conjunto de objetos de datos D , cualquier función bien definida (de valor único) de K a D ∪ { null } define un tipo de referencia, donde null es la imagen de una clave que no se refiere a nada significativo.
Una representación alternativa de dicha función es un grafo dirigido llamado grafo de alcanzabilidad . En este grafo, cada dato está representado por un vértice y existe una arista de u a v si el dato en u se refiere al dato en v . El grado máximo de salida es uno. Estos grafos son valiosos en la recolección de basura , donde se pueden usar para separar los objetos accesibles de los inaccesibles .
Almacenamiento externo e interno
En muchas estructuras de datos, los objetos grandes y complejos se componen de objetos más pequeños. Estos objetos se almacenan normalmente de dos maneras:
- Con el almacenamiento interno, el contenido del objeto más pequeño se guarda dentro del objeto más grande.
- Con el almacenamiento externo, los objetos más pequeños se asignan en su propia ubicación, y el objeto más grande solo almacena referencias a ellos.
El almacenamiento interno suele ser más eficiente, ya que implica un coste de espacio para las referencias y los metadatos de asignación dinámica , así como un coste de tiempo asociado a la desreferenciación y a la asignación de memoria para los objetos más pequeños. El almacenamiento interno también mejora la localidad de referencia al mantener diferentes partes de un mismo objeto grande cerca unas de otras en la memoria. Sin embargo, existen diversas situaciones en las que se prefiere el almacenamiento externo:
- Si la estructura de datos es recursiva , es decir, puede contenerse a sí misma, esto no puede representarse internamente.
- Si el objeto de mayor tamaño se almacena en un área con espacio limitado, como la pila, podemos evitar quedarnos sin espacio de almacenamiento guardando los objetos componentes grandes en otra región de memoria y haciendo referencia a ellos mediante referencias.
- Si los objetos más pequeños pueden variar de tamaño, a menudo resulta inconveniente o costoso redimensionar el objeto más grande para que aún pueda contenerlos.
- Las referencias suelen ser más fáciles de usar y se adaptan mejor a los nuevos requisitos.
Algunos lenguajes, como Java , Smalltalk , Python y Scheme , no admiten almacenamiento interno. En estos lenguajes, se accede a todos los objetos de forma uniforme mediante referencias.
Soporte de idiomas
Asamblea
En lenguaje ensamblador , es común expresar referencias mediante direcciones de memoria directas o índices en tablas. Si bien estos métodos funcionan, su uso resulta algo complejo, ya que una dirección no proporciona información sobre el valor al que apunta, ni siquiera su tamaño o cómo interpretarlo; dicha información está codificada en la lógica del programa. Como consecuencia, pueden producirse interpretaciones erróneas en programas incorrectos, lo que genera errores desconcertantes.
Ceceo
Una de las primeras referencias opacas fue la de la celda cons del lenguaje Lisp , que es simplemente un registro que contiene dos referencias a otros objetos Lisp, incluyendo posiblemente otras celdas cons. Esta estructura simple se usa comúnmente para construir listas enlazadas simples , pero también puede usarse para construir árboles binarios simples y las llamadas "listas punteadas", que terminan no con una referencia nula sino con un valor.
C/C++
El puntero sigue siendo uno de los tipos de referencia más populares en la actualidad. Es similar a la representación en lenguaje ensamblador de una dirección sin formato, con la diferencia de que lleva un tipo de dato estático que se puede usar en tiempo de compilación para asegurar que los datos a los que apunta no se interpreten erróneamente. Sin embargo, debido a que C tiene un sistema de tipos débil que puede ser vulnerado mediante conversiones explícitas (conversiones entre varios tipos de puntero y entre tipos de puntero y enteros), la interpretación errónea sigue siendo posible, aunque más difícil. Su sucesor, C++, intentó aumentar la seguridad de tipos de los punteros con nuevos operadores de conversión, un tipo de referencia& y punteros inteligentes en su biblioteca estándar , pero aún conservó la capacidad de eludir estos mecanismos de seguridad por motivos de compatibilidad.
Fortran
Fortran no tiene una representación explícita de referencias, pero las utiliza implícitamente en su semántica de llamada por referencia . Una referencia en Fortran se puede entender como un alias de otro objeto, como una variable escalar o una fila o columna de una matriz. No existe sintaxis para desreferenciar la referencia ni para manipular directamente el contenido del objeto referenciado. Las referencias en Fortran pueden ser nulas. Al igual que en otros lenguajes, estas referencias facilitan el procesamiento de estructuras dinámicas, como listas enlazadas, colas y árboles.
Lenguajes orientados a objetos
Varios lenguajes orientados a objetos, como Eiffel , Java , C# y Visual Basic , han adoptado un tipo de referencia mucho más opaco, generalmente denominado simplemente referencia . Estas referencias tienen tipos similares a los punteros de C que indican cómo interpretar los datos a los que hacen referencia, pero son seguras en cuanto a tipos, ya que no pueden interpretarse como una dirección sin procesar y no se permiten conversiones inseguras. Las referencias se utilizan ampliamente para acceder a objetos y asignarles nombres . También se utilizan en llamadas a funciones/ métodos o en el paso de mensajes, y los contadores de referencias se utilizan con frecuencia para la recolección de basura de objetos no utilizados.
Lenguajes funcionales
En Standard ML , OCaml y muchos otros lenguajes funcionales, la mayoría de los valores son persistentes: no se pueden modificar mediante asignación. Las "celdas de referencia" asignables proporcionan variables mutables , datos que se pueden modificar. Dichas celdas de referencia pueden contener cualquier valor, por lo que se les asigna el tipo polimórficoα ref , donde αse reemplaza con el tipo del valor al que apunta. Estas referencias mutables pueden apuntar a diferentes objetos a lo largo de su ciclo de vida. Por ejemplo, esto permite construir estructuras de datos circulares. La celda de referencia es funcionalmente equivalente a un array mutable de longitud 1.
Para garantizar la seguridad y la eficiencia de las implementaciones, en ML no se pueden realizar conversiones de tipo de referencias ni operaciones aritméticas con punteros. En el paradigma funcional, muchas estructuras que se representarían mediante punteros en un lenguaje como C se representan mediante otros mecanismos, como el potente mecanismo de tipos de datos algebraicos . De este modo, el programador puede disfrutar de ciertas propiedades (como la garantía de inmutabilidad) durante la programación, aunque el compilador utilice punteros de máquina internamente.
Perl/PHP
Perl admite referencias duras, que funcionan de forma similar a las de otros lenguajes, y referencias simbólicas , que son simplemente cadenas de caracteres que contienen los nombres de las variables. Cuando se desreferencia un valor que no es una referencia dura, Perl lo considera una referencia simbólica y devuelve la variable con el nombre especificado por dicho valor. [ 3 ] PHP cuenta con una característica similar en su $$varsintaxis. [ 4 ]
Véase también
Referencias
- 1 2 Sherman, Mark S. (abril de 1985). Paragon: Un lenguaje que utiliza jerarquías de tipos para la especificación, implementación y selección de tipos de datos abstractos . Springer Science & Business Media. pág. 175. ISBN 978-3-540-15212-5.
- ↑ "Referencia (Plataforma Java SE 7)" . docs.oracle.com . Consultado el 10 de mayo de 2022 .
- ↑ "perlref" . perldoc.perl.org . Consultado el 19 de agosto de 2013 .
- ↑ "Variables variables - Manual" . PHP . Consultado el 19 de agosto de 2013 .
Enlaces externos
- Diversión con punteros con Binky: Introducción a los punteros en un vídeo educativo de 3 minutos – Biblioteca de Educación en Ciencias de la Computación de Stanford
- Tipos de datos
- conceptos de lenguajes de programación
- Tipos primitivos