Referencia (informática)

Ajustar Compartir Imprimir Citar
Tipo de datos que permite a un programa acceder indirectamente a un valor particular en la memoria

En la programación de computadoras, una referencia es un valor que permite que un programa acceda indirectamente a un dato en particular, como el valor de una variable o un registro, en la computadora. memoria o en algún otro dispositivo de almacenamiento. Se dice que la referencia remite al dato, y acceder al dato se llama desreferenciar la referencia. Una referencia es distinta del dato mismo.

Una referencia es un tipo de datos abstracto y se puede implementar de muchas formas. Por lo general, una referencia se refiere a los 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 esta razón, a menudo se dice que una referencia "apunta a" los datos. Otras implementaciones incluyen un desplazamiento (diferencia) entre la dirección del dato y alguna "base" dirección, un índice o identificador utilizado en una operación de búsqueda en una matriz o tabla, un identificador de sistema operativo, una dirección física en un dispositivo de almacenamiento o una dirección de red como una URL.

Representación oficial

Una referencia R es un valor que admite una operación, desreferencia(R), que arroja un valor. Por lo general, la referencia se escribe para que devuelva valores de un tipo específico, por ejemplo:

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.

Usar

Las referencias se utilizan ampliamente en la programación, especialmente para pasar de manera eficiente datos grandes o mutables como argumentos a procedimientos, o para compartir dichos datos entre varios 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 enlazados, 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 los datos a través de ellos y no es necesario mover los datos en sí. También facilitan el intercambio de datos entre diferentes áreas de código; cada uno guarda una referencia a él.

Las referencias pueden causar una complejidad significativa en un programa, en parte debido a la posibilidad de referencias colgantes y salvajes y en parte porque la topología de datos con referencias es un gráfico dirigido, cuyo análisis puede ser bastante complicado. No obstante, las referencias siguen siendo más sencillas de analizar que los punteros debido a la ausencia de aritmética de punteros.

El mecanismo de las referencias, si varía en la implementación, es una característica fundamental del lenguaje de programación común a casi todos los lenguajes de programación modernos. Incluso algunos lenguajes que no admiten el uso directo de referencias tienen algún uso interno o implícito. Por ejemplo, la convención de llamada de llamada por referencia se puede implementar con el uso explícito o implícito de referencias.

Ejemplos

Los punteros son el tipo de referencia más primitivo. Debido a su íntima relación con el hardware subyacente, son uno de los tipos de referencias más potentes y eficientes. Sin embargo, también debido a esta relación, los punteros requieren una sólida comprensión por parte del programador de los detalles de la arquitectura de la memoria. Debido a que los punteros almacenan la dirección de una ubicación de memoria, en lugar de un valor directamente, el uso inapropiado de los punteros puede generar un comportamiento indefinido en un programa, particularmente debido a punteros colgantes o punteros salvajes. Los punteros inteligentes son estructuras de datos opacas que actúan como punteros, pero solo se puede acceder a ellos a través de métodos particulares.

Un identificador es una referencia abstracta y se puede representar de varias formas. 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 del archivo. Por lo general, representa tanto el archivo en sí mismo, como cuando se solicita un bloqueo en el archivo, y una posición específica dentro del contenido del archivo, como cuando se lee un archivo.

En computación distribuida, la referencia puede contener más que una dirección o identificador; también puede incluir una especificación integrada de los protocolos de red utilizados para localizar y acceder al objeto al que se hace referencia, la forma en que se codifica o serializa la información. Así, por ejemplo, una descripción WSDL de un servicio web remoto puede verse como una forma de referencia; incluye una especificación completa de cómo localizar y vincularse a un servicio web en particular. Una referencia a un objeto distribuido vivo es otro ejemplo: es una especificación completa sobre cómo construir un pequeño componente de software llamado proxy que posteriormente participará en una interacción entre pares, y a través del cual la máquina local puede obtener acceso a los datos que se replican o existen solo como un flujo de mensajes débilmente consistente. En todos estos casos, la referencia incluye el conjunto completo de instrucciones, o una receta, sobre cómo acceder a los datos; en este sentido, cumple el mismo propósito que un identificador o dirección en la memoria.

Si tenemos un conjunto de claves K y un conjunto de objetos de datos D, cualquier función bien definida (de un solo valor) de K a D ∪ {null} define un tipo de referencia, donde null es la imagen de una clave que no hace referencia a nada significativo.

Una representación alternativa de dicha función es un gráfico dirigido llamado gráfico de accesibilidad. Aquí, cada dato está representado por un vértice y hay una arista de u a v si el dato en u se refiere al dato en v. El máximo grado de salida es uno. Estos gráficos 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 normalmente se almacenan en una de dos formas:

  1. Con almacenamiento interno, el contenido del objeto más pequeño se almacena dentro del objeto más grande.
  2. Con almacenamiento externo, los objetos más pequeños se asignan en su propia ubicación, y el objeto más grande sólo almacena referencias a ellos.

El almacenamiento interno suele ser más eficiente, porque hay un costo de espacio para las referencias y los metadatos de asignación dinámica, y un costo de tiempo asociado con la desreferenciación de una referencia y la asignación de memoria para los objetos más pequeños. El almacenamiento interno también mejora la localidad de referencia al mantener juntas en la memoria diferentes partes del mismo objeto grande. Sin embargo, hay una variedad de situaciones en las que se prefiere el almacenamiento externo:

Algunos lenguajes, como Java, Smalltalk, Python y Scheme, no admiten almacenamiento interno. En estos lenguajes, se accede uniformemente a todos los objetos a través de referencias.

Soporte de idiomas

Montaje

En lenguaje ensamblador, es típico expresar referencias utilizando direcciones de memoria sin procesar o índices en tablas. Estos funcionan, pero son un poco complicados de usar, porque una dirección no dice nada sobre el valor al que apunta, ni siquiera qué tan grande es o cómo interpretarlo; dicha información está codificada en la lógica del programa. El resultado es que pueden ocurrir malas interpretaciones en programas incorrectos, causando errores desconcertantes.

Ceceo

Una de las primeras referencias opacas fue la de la celda contras del lenguaje Lisp, que es simplemente un registro que contiene dos referencias a otros objetos Lisp, incluidas posiblemente otras celdas contras. Esta estructura simple se usa más comúnmente para construir listas enlazadas individualmente, pero también se puede usar para construir árboles binarios simples y las llamadas "listas punteadas", que no terminan con una referencia nula sino con un valor.

C/C++

El puntero sigue siendo uno de los tipos de referencias más populares en la actualidad. Es similar a la representación de ensamblaje de una dirección sin procesar, excepto que lleva un tipo de datos estático que se puede usar en tiempo de compilación para garantizar que los datos a los que hace referencia no se malinterpreten. Sin embargo, debido a que C tiene un sistema de tipos débil que se puede violar mediante conversiones (conversiones explícitas entre varios tipos de punteros y entre tipos de punteros y enteros), aún es posible una interpretación errónea, aunque más difícil. Su sucesor, C++, intentó aumentar la seguridad de los tipos de punteros con nuevos operadores de conversión, un tipo de referencia & y punteros inteligentes en su biblioteca estándar, pero aún conservaba la capacidad de eludir estos mecanismos de seguridad por compatibilidad.

Fortran

Fortran no tiene una representación explícita de referencias, pero las usa implícitamente en su semántica de llamadas de llamada por referencia. Una referencia de Fortran se considera mejor como un alias de otro objeto, como una variable escalar o una fila o columna de una matriz. No hay sintaxis para desreferenciar la referencia o manipular los contenidos del referente directamente. Las referencias de Fortran pueden ser nulas. Como 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 como punteros C que indican cómo interpretar los datos a los que hacen referencia, pero tienen seguridad de tipos en el sentido de que no se pueden interpretar como una dirección sin formato y no se permiten conversiones no seguras. Las referencias se utilizan ampliamente para acceder y asignar objetos. Las referencias también se usan en llamadas a funciones/métodos o en el paso de mensajes, y los recuentos de referencias se usan con frecuencia para realizar la recolección de elementos no utilizados 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. Asignables "celdas de referencia" proporcionar variables mutables, datos que se pueden modificar. Estas celdas de referencia pueden contener cualquier valor, por lo que se les asigna el tipo polimórfico α ref, donde α debe reemplazarse con el tipo de valor señalado. Estas referencias mutables pueden apuntar a diferentes objetos a lo largo de su vida. Por ejemplo, esto permite construir estructuras de datos circulares. La celda de referencia es funcionalmente equivalente a una matriz mutable de longitud 1.

Para preservar la seguridad y las implementaciones eficientes, las referencias no se pueden convertir en tipo en ML, ni se puede realizar la aritmética de punteros. Es importante señalar que en el paradigma funcional, muchas estructuras que se representarían mediante punteros en un lenguaje como C se representan mediante otras funciones, como el potente mecanismo de tipos de datos algebraicos. Entonces, el programador puede disfrutar de ciertas propiedades (como la garantía de inmutabilidad) mientras programa, aunque el compilador a menudo usa punteros de máquina 'bajo el capó'.

Perl/PHP

Perl admite referencias duras, que funcionan de manera similar a las de otros idiomas, y referencias simbólicas, que son solo valores de cadena que contienen los nombres de las variables. Cuando se elimina la referencia de un valor que no es una referencia fija, Perl lo considera una referencia simbólica y le da a la variable el nombre dado por el valor. PHP tiene una característica similar en la forma de su sintaxis $$var.