stringtranslate.com

Puntero (programación informática)

Considero que las declaraciones de asignación y las variables indicadoras se encuentran entre los "tesoros más valiosos" de la informática.

Donald Knuth , Programación estructurada, con ir a Declaraciones [1]

Un puntero a que apunta a la dirección de memoria asociada con una variable b, es decir, a contiene la dirección de memoria 1008 de la variable b . En este diagrama, la arquitectura informática utiliza el mismo espacio de direcciones y datos primitivos tanto para punteros como para no punteros; esta necesidad no debería ser el caso.

En informática , un puntero es un objeto en muchos lenguajes de programación que almacena una dirección de memoria . Este puede ser el de otro valor ubicado en la memoria de la computadora , o en algunos casos, el de un hardware de computadora mapeado en memoria . Un puntero hace referencia a una ubicación en la memoria y obtener el valor almacenado en esa ubicación se conoce como desreferenciar el puntero. Como analogía, el número de página del índice de un libro podría considerarse un indicador de la página correspondiente; La desreferenciación de dicho puntero se haría pasando a la página con el número de página dado y leyendo el texto que se encuentra en esa página. El formato y el contenido reales de una variable de puntero dependen de la arquitectura de la computadora subyacente .

El uso de punteros mejora significativamente el rendimiento de operaciones repetitivas, como atravesar estructuras de datos iterables (por ejemplo , cadenas , tablas de búsqueda , tablas de control y estructuras de árbol ). En particular, suele ser mucho más económico en tiempo y espacio copiar y desreferenciar punteros que copiar y acceder a los datos a los que apuntan los punteros.

Los punteros también se utilizan para contener las direcciones de los puntos de entrada de las subrutinas llamadas en la programación de procedimientos y para vincular en tiempo de ejecución a bibliotecas de vínculos dinámicos (DLL) . En la programación orientada a objetos , los punteros a funciones se utilizan para vincular métodos , a menudo utilizando tablas de métodos virtuales .

Un puntero es una implementación simple y más concreta del tipo de datos de referencia más abstracto . Varios lenguajes, especialmente los de bajo nivel , soportan algún tipo de puntero, aunque algunos tienen más restricciones en su uso que otros. Si bien "puntero" se ha utilizado para referirse a referencias en general, se aplica más apropiadamente a estructuras de datos cuya interfaz permite explícitamente manipular el puntero (aritméticamente mediantearitmética de punteros ) como dirección de memoria, a diferencia de unacookie mágicaocapacidadque no lo permite. [ cita necesaria ]Debido a que los punteros permiten el acceso tanto protegido como desprotegido adirecciones de memoria, existen riesgos asociados con su uso, particularmente en el último caso. Los punteros primitivos suelen almacenarse en un formato similar a unnúmero entero; sin embargo, intentar eliminar la referencia o "buscar" un puntero cuyo valor no sea una dirección de memoria válida podría provocar que el programa falle(o contenga datos no válidos). Para aliviar este problema potencial, como cuestión deseguridad de tipos, los punteros se consideran un tipo separado parametrizado por el tipo de datos al que apuntan, incluso si la representación subyacente es un número entero. También se pueden tomar otras medidas (comovalidaciónyverificación de límites) para verificar que la variable de puntero contenga un valor que sea una dirección de memoria válida y esté dentro del rango numérico que el procesador es capaz de direccionar.

Historia

En 1955, la informática soviética ucraniana Kateryna Yushchenko inventó la dirección (lenguaje de programación) que hacía posible el direccionamiento indirecto y las direcciones del más alto rango, análogas a los punteros. Este lenguaje fue ampliamente utilizado en las computadoras de la Unión Soviética. Sin embargo, era desconocido fuera de la Unión Soviética y normalmente se le atribuye a Harold Lawson la invención, en 1964, del puntero. [2] En 2000, Lawson recibió el premio Computer Pioneer Award otorgado por el IEEE "[p]or inventar la variable de puntero e introducir este concepto en PL/I, proporcionando así, por primera vez, la capacidad de tratar de manera flexible listas enlazadas en un lenguaje de alto nivel de propósito general". [3] Su artículo fundamental sobre los conceptos apareció en la edición de junio de 1967 del CACM titulado: PL/I List Processing. Según el Oxford English Dictionary , la palabra puntero apareció por primera vez impresa como puntero de pila en un memorando técnico de System Development Corporation .

Descripción formal

En informática , un puntero es una especie de referencia .

Una primitiva de datos (o simplemente primitiva ) es cualquier dato que se puede leer o escribir en la memoria de la computadora usando un acceso a la memoria (por ejemplo, tanto un byte como una palabra son primitivas).

Un agregado de datos (o simplemente agregado ) es un grupo de primitivos que son lógicamente contiguos en la memoria y que se ven colectivamente como un dato (por ejemplo, un agregado podría consistir en 3 bytes lógicamente contiguos, cuyos valores representan las 3 coordenadas de un punto en el espacio). Cuando un agregado está enteramente compuesto por el mismo tipo de primitivo, el agregado puede denominarse matriz ; en cierto sentido, una palabra primitiva de varios bytes es una matriz de bytes y algunos programas usan palabras de esta manera.

Un puntero es un concepto de programación utilizado en informática para hacer referencia o señalar una ubicación de memoria que almacena un valor o un objeto. Es esencialmente una variable que almacena la dirección de memoria de otra variable o estructura de datos en lugar de almacenar los datos en sí.

Los punteros se utilizan comúnmente en lenguajes de programación que admiten la manipulación directa de la memoria, como C y C++. Permiten a los programadores trabajar con la memoria directamente, lo que permite una gestión eficiente de la memoria y estructuras de datos más complejas. Al utilizar punteros, puede acceder y modificar datos ubicados en la memoria, pasar datos de manera eficiente entre funciones y crear estructuras de datos dinámicas como listas vinculadas, árboles y gráficos.

En términos más simples, puedes pensar en un puntero como una flecha que apunta a un lugar específico en la memoria de una computadora, permitiéndote interactuar con los datos almacenados en esa ubicación.

Un puntero de memoria (o simplemente un puntero ) es una primitiva cuyo valor está destinado a ser utilizado como dirección de memoria; se dice que un puntero apunta a una dirección de memoria . También se dice que un puntero apunta a un dato [en la memoria] cuando el valor del puntero es la dirección de memoria del dato.

De manera más general, un puntero es un tipo de referencia , y se dice que un puntero hace referencia a un dato almacenado en algún lugar de la memoria ; obtener ese dato es desreferenciar el puntero . La característica que separa los punteros de otros tipos de referencias es que el valor de un puntero debe interpretarse como una dirección de memoria, lo cual es un concepto de bajo nivel.

Las referencias sirven como nivel de direccionamiento indirecto: el valor de un puntero determina qué dirección de memoria (es decir, qué dato) se utilizará en un cálculo. Debido a que la dirección indirecta es un aspecto fundamental de los algoritmos, los punteros a menudo se expresan como un tipo de datos fundamental en los lenguajes de programación ; En lenguajes de programación estáticos (o fuertemente tipados), el tipo de puntero determina el tipo de dato al que apunta el puntero.

Raíces arquitectónicas

Los punteros son una abstracción muy fina además de las capacidades de direccionamiento proporcionadas por la mayoría de las arquitecturas modernas . En el esquema más simple, se asigna una dirección o un índice numérico a cada unidad de memoria del sistema, donde la unidad suele ser un byte o una palabra , dependiendo de si la arquitectura es direccionable por bytes o por palabras . transformando efectivamente toda la memoria en una matriz muy grande . Luego, el sistema también proporcionaría una operación para recuperar el valor almacenado en la unidad de memoria en una dirección determinada (generalmente utilizando los registros de propósito general de la máquina ).

En el caso habitual, un puntero es lo suficientemente grande como para contener más direcciones que unidades de memoria en el sistema. Esto introduce la posibilidad de que un programa intente acceder a una dirección que no corresponde a ninguna unidad de memoria, ya sea porque no hay suficiente memoria instalada (es decir, más allá del rango de memoria disponible) o porque la arquitectura no admite dichas direcciones. El primer caso, en determinadas plataformas como la arquitectura Intel x86 , puede denominarse fallo de segmentación (segfault). El segundo caso es posible en la implementación actual de AMD64 , donde los punteros tienen una longitud de 64 bits y las direcciones solo se extienden a 48 bits. Los punteros deben ajustarse a ciertas reglas (direcciones canónicas), por lo que si se desreferencia un puntero no canónico, el procesador genera una falla de protección general .

Por otro lado, algunos sistemas tienen más unidades de memoria que direcciones. En este caso, se emplea un esquema más complejo, como segmentación de memoria o paginación , para utilizar diferentes partes de la memoria en diferentes momentos. Las últimas encarnaciones de la arquitectura x86 admiten hasta 36 bits de direcciones de memoria física, que se asignaron al espacio de direcciones lineales de 32 bits a través del mecanismo de paginación PAE . Por lo tanto, sólo se puede acceder a 1/16 de la memoria total posible a la vez. Otro ejemplo de la misma familia de ordenadores fue el modo protegido de 16 bits del procesador 80286 , que, aunque sólo soportaba 16 MB de memoria física, podía acceder hasta 1 GB de memoria virtual, pero la combinación de dirección de 16 bits y segmento Los registros hacían engorroso el acceso a más de 64 KB en una estructura de datos.

Para proporcionar una interfaz coherente, algunas arquitecturas proporcionan E/S asignadas en memoria , lo que permite que algunas direcciones se refieran a unidades de memoria mientras que otras se refieren a registros de dispositivos de otros dispositivos en la computadora. Existen conceptos análogos, como desplazamientos de archivos, índices de matrices y referencias a objetos remotos, que tienen los mismos propósitos que las direcciones para otros tipos de objetos.

Usos

Los punteros se admiten directamente sin restricciones en lenguajes como PL/I , C , C++ , Pascal , FreeBASIC e implícitamente en la mayoría de los lenguajes ensambladores . Se utilizan principalmente para construir referencias , que a su vez son fundamentales para construir casi todas las estructuras de datos , así como para pasar datos entre diferentes partes de un programa.

En los lenguajes de programación funcionales que dependen en gran medida de listas, las referencias de datos se administran de manera abstracta mediante el uso de construcciones primitivas como cons y los elementos correspondientes car y cdr , que pueden considerarse como punteros especializados al primer y segundo componente de una celda de cons. Esto da lugar a algo del "sabor" idiomático de la programación funcional. Al estructurar datos en tales listas de contras , estos lenguajes facilitan medios recursivos para construir y procesar datos, por ejemplo, accediendo recursivamente a los elementos principales y finales de listas de listas; por ejemplo "llevando el auto del cdr del cdr". Por el contrario, la gestión de la memoria basada en la desreferenciación de punteros en alguna aproximación de una matriz de direcciones de memoria facilita el tratamiento de las variables como ranuras a las que se pueden asignar datos de forma imperativa .

Cuando se trata de matrices, la operación de búsqueda crítica normalmente implica una etapa llamada cálculo de dirección que implica construir un puntero al elemento de datos deseado en la matriz. En otras estructuras de datos, como las listas enlazadas , los punteros se utilizan como referencias para vincular explícitamente una parte de la estructura a otra.

Los punteros se utilizan para pasar parámetros por referencia. Esto es útil si el programador desea que las modificaciones de una función a un parámetro sean visibles para quien llama a la función. Esto también es útil para devolver múltiples valores de una función.

Los punteros también se pueden utilizar para asignar y desasignar variables y matrices dinámicas en la memoria. Dado que una variable a menudo se volverá redundante después de haber cumplido su propósito, es un desperdicio de memoria conservarla y, por lo tanto, es una buena práctica desasignarla (usando la referencia del puntero original) cuando ya no sea necesaria. De lo contrario, se puede producir una pérdida de memoria (donde la memoria libre disponible disminuye gradualmente o, en casos graves, rápidamente, debido a la acumulación de numerosos bloques de memoria redundantes).

punteros C

La sintaxis básica para definir un puntero es: [4]

int * ptr ; 

Esto declara ptrcomo identificador de un objeto del siguiente tipo:

Esto generalmente se expresa de manera más sucinta como " ptres un puntero a int".

Debido a que el lenguaje C no especifica una inicialización implícita para objetos de duración de almacenamiento automático, [5] a menudo se debe tener cuidado para garantizar que la dirección a la que ptrapunta sea válida; Es por eso que a veces se sugiere que un puntero se inicialice explícitamente con el valor del puntero nulo , que tradicionalmente se especifica en C con la macro estandarizada NULL: [6]

int * ptr = NULL ;   

Eliminar la referencia a un puntero nulo en C produce un comportamiento indefinido , [7] que podría ser catastrófico. Sin embargo, la mayoría de las implementaciones [ cita requerida ] simplemente detienen la ejecución del programa en cuestión, generalmente con un error de segmentación .

Sin embargo, inicializar punteros innecesariamente podría dificultar el análisis del programa, ocultando así errores.

En cualquier caso, una vez declarado un puntero, el siguiente paso lógico es que apunte a algo:

int a = 5 ; int * ptr = NULL ;      ptr = &a ;  

Esto asigna el valor de la dirección de aa ptr. Por ejemplo, si ase almacena en la ubicación de memoria 0x8130, el valor de ptrserá 0x8130 después de la asignación. Para desreferenciar el puntero, se vuelve a utilizar un asterisco:

* ptr = 8 ;  

Esto significa tomar el contenido de ptr(que es 0x8130), "localizar" esa dirección en la memoria y establecer su valor en 8. Si ase accede nuevamente más adelante, su nuevo valor será 8.

Este ejemplo puede resultar más claro si se examina la memoria directamente. Supongamos que aestá ubicado en la dirección 0x8130 en memoria y ptren 0x8134; Supongamos también que se trata de una máquina de 32 bits, de modo que un int tiene 32 bits de ancho. Lo siguiente es lo que quedaría en la memoria después de ejecutar el siguiente fragmento de código:

int a = 5 ; int * ptr = NULL ;      

(El puntero NULL que se muestra aquí es 0x00000000). Asignando la dirección de aa ptr:

ptr = &a ;  

produce los siguientes valores de memoria:

Luego, desreferenciando ptrmediante codificación:

* ptr = 8 ;  

la computadora tomará el contenido de ptr(que es 0x8130), 'localizará' esa dirección y asignará 8 a esa ubicación, lo que producirá la siguiente memoria:

Claramente, acceder aarrojará el valor de 8 porque la instrucción anterior modificó el contenido de amediante el puntero ptr.

Uso en estructuras de datos.

Al configurar estructuras de datos como listas , colas y árboles, es necesario tener punteros para ayudar a gestionar cómo se implementa y controla la estructura. Ejemplos típicos de punteros son los punteros de inicio, los punteros de finalización y los punteros de pila . Estos punteros pueden ser absolutos (la dirección física real o una dirección virtual en la memoria virtual ) o relativos (un desplazamiento de una dirección inicial absoluta ("base") que normalmente usa menos bits que una dirección completa, pero generalmente requerirá uno adicional. operación aritmética para resolver).

Las direcciones relativas son una forma de segmentación de memoria manual y comparten muchas de sus ventajas y desventajas. Se puede utilizar un desplazamiento de dos bytes, que contiene un entero sin signo de 16 bits, para proporcionar un direccionamiento relativo de hasta 64 KiB (2 x 16 bytes) de una estructura de datos. Esto se puede ampliar fácilmente a 128, 256 o 512 KiB si la dirección apuntada se fuerza a alinearse en un límite de media palabra, palabra o palabra doble (pero, al requerir una operación bit a bit adicional de "desplazamiento a la izquierda" , en 1, 2 o 3 bits (para ajustar el desplazamiento en un factor de 2, 4 u 8, antes de su adición a la dirección base). Generalmente, sin embargo, tales esquemas causan muchos problemas y, por conveniencia para el programador, se prefieren las direcciones absolutas (y subyacentes a ellas, un espacio de direcciones plano ).

Se puede utilizar un desplazamiento de un byte, como el valor ASCII hexadecimal de un carácter (por ejemplo, X'29') para señalar un valor entero (o índice) alternativo en una matriz (por ejemplo, X'01'). De esta manera, los caracteres se pueden traducir de manera muy eficiente desde ' datos sin procesar ' a un índice secuencial utilizable y luego a una dirección absoluta sin una tabla de búsqueda .

matrices C

En C, la indexación de matrices se define formalmente en términos de aritmética de punteros; es decir, la especificación del lenguaje requiere que array[i]sea equivalente a *(array + i). [8] Por lo tanto, en C, las matrices pueden considerarse como punteros a áreas consecutivas de memoria (sin espacios), [8] y la sintaxis para acceder a las matrices es idéntica a la que se puede usar para desreferenciar punteros. Por ejemplo, una matriz arrayse puede declarar y utilizar de la siguiente manera:

matriz int [ 5 ]; /* Declara 5 enteros contiguos */ int * ptr = array ; /* Las matrices se pueden utilizar como punteros */ ptr [ 0 ] = 1 ; /* Los punteros se pueden indexar con sintaxis de matriz */ * ( matriz + 1 ) = 2 ; /* Se puede desreferenciar las matrices con sintaxis de puntero */ * ( 1 + matriz ) = 2 ; /* La suma de punteros es conmutativa */ 2 [ matriz ] = 4 ; /* El operador de subíndice es conmutativo */                      

Esto asigna un bloque de cinco números enteros y le da un nombre al bloque array, que actúa como un puntero al bloque. Otro uso común de los punteros es apuntar a la memoria asignada dinámicamente desde malloc , que devuelve un bloque de memoria consecutivo de no menos del tamaño solicitado que se puede usar como una matriz.

Si bien la mayoría de los operadores de matrices y punteros son equivalentes, el resultado del sizeofoperador difiere. En este ejemplo, sizeof(array)se evaluará como 5*sizeof(int)(el tamaño de la matriz), mientras que sizeof(ptr)se evaluará como sizeof(int*)el tamaño del puntero en sí.

Los valores predeterminados de una matriz se pueden declarar como:

matriz int [ 5 ] = { 2 , 4 , 3 , 1 , 5 };       

Si está ubicado en la memoria a partir de la dirección 0x1000 en una máquina little-endianarray de 32 bits, entonces la memoria contendrá lo siguiente (los valores están en hexadecimal , como las direcciones):

Aquí se representan cinco números enteros: 2, 4, 3, 1 y 5. Estos cinco números enteros ocupan 32 bits (4 bytes), cada uno con el byte menos significativo almacenado primero (esta es una arquitectura de CPU little-endian ) y se almacenan consecutivamente. comenzando en la dirección 0x1000.

La sintaxis para C con punteros es:

El último ejemplo es cómo acceder al contenido de array. Desglosándolo:

lista enlazada C

A continuación se muestra un ejemplo de definición de una lista enlazada en C.

/* la lista enlazada vacía está representada por NULL * o algún otro valor centinela */ #define EMPTY_LIST NULLenlace de estructura { void * datos ; /* datos de este enlace */ struct link * next ; /* siguiente enlace; EMPTY_LIST si no hay ninguno */ };         

Esta definición recursiva de puntero es esencialmente la misma que la definición recursiva de referencia del lenguaje de programación Haskell :

 enlace de datos a = nulo | Contras a ( Enlace a )         

Niles la lista vacía y Cons a (Link a)es una celda de tipo contrasa con otro enlace también de tipo a.

La definición con referencias, sin embargo, está verificada y no utiliza valores de señal potencialmente confusos. Por esta razón, las estructuras de datos en C generalmente se manejan mediante funciones contenedoras , cuya corrección se verifica cuidadosamente.

Pasar por dirección usando punteros

Se pueden utilizar punteros para pasar variables por su dirección, lo que permite cambiar su valor. Por ejemplo, considere el siguiente código C :

/* se puede cambiar una copia de int n dentro de la función sin afectar el código de llamada */ void passByValue ( int n ) { n = 12 ; }      /* en su lugar se pasa un puntero m. No se crea ninguna copia del valor señalado por m */ void passByAddress ( int * m ) { * m = 14 ; }      int principal ( vacío ) { int x = 3 ;       /* pasa una copia del valor de x como argumento */ passByValue ( x ); // el valor fue cambiado dentro de la función, pero x sigue siendo 3 de aquí en adelante   /* pasa la dirección de x como argumento */ passByAddress ( & x ); // x fue realmente cambiado por la función y ahora es igual a 14 aquí   devolver 0 ; } 

Asignación de memoria dinámica

En algunos programas, la cantidad de memoria requerida depende de lo que el usuario pueda ingresar. En tales casos, el programador necesita asignar memoria dinámicamente. Esto se hace asignando memoria en el montón en lugar de en la pila , donde normalmente se almacenan las variables (aunque las variables también se pueden almacenar en los registros de la CPU). La asignación de memoria dinámica sólo se puede realizar mediante punteros y no se pueden dar nombres, como sucede con las variables comunes.

Los punteros se utilizan para almacenar y administrar las direcciones de bloques de memoria asignados dinámicamente . Estos bloques se utilizan para almacenar objetos de datos o matrices de objetos. La mayoría de los lenguajes estructurados y orientados a objetos proporcionan un área de memoria, llamada montón o almacén libre , desde la cual los objetos se asignan dinámicamente.

El código C de ejemplo siguiente ilustra cómo se asignan y hacen referencia dinámicamente los objetos de estructura. La biblioteca C estándar proporciona la función malloc()para asignar bloques de memoria del montón. Toma el tamaño de un objeto para asignarlo como parámetro y devuelve un puntero a un bloque de memoria recién asignado adecuado para almacenar el objeto, o devuelve un puntero nulo si la asignación falló.

/* Artículo del inventario de piezas */ struct Item { int id ; /* Número de pieza */ char * nombre ; /* Nombre de la pieza */ costo flotante ; /* Costo */ };            /* Asigna e inicializa un nuevo objeto Item */ struct Item * make_item ( const char * name ) { struct Item * item ;           /* Asigna un bloque de memoria para un nuevo objeto Item */ item = malloc ( sizeof ( struct Item )); si ( elemento == NULL ) devuelve NULL ;           /* Inicializa los miembros del nuevo artículo */ memset ( item , 0 , sizeof ( struct Item )); elemento -> identificación = -1 ; elemento -> nombre = NULL ; artículo -> costo = 0.0 ;              /* Guardar una copia del nombre en el nuevo elemento */ item -> nombre = malloc ( strlen ( nombre ) + 1 ); if ( artículo -> nombre == NULL ) { gratis ( artículo ); devolver NULO ; } strcpy ( elemento -> nombre , nombre );                 /* Devuelve el objeto Artículo recién creado */ devuelve el artículo ; }  

El siguiente código ilustra cómo los objetos de memoria se desasignan dinámicamente, es decir, se devuelven al montón o al almacén libre. La biblioteca C estándar proporciona la función free()para desasignar un bloque de memoria previamente asignado y devolverlo al montón.

/* Desasignar un objeto Item */ void destroy_item ( struct Item * item ) { /* Comprobar si hay un puntero de objeto nulo */ if ( item == NULL ) return ;           /* Desasigna la cadena de nombre guardada dentro del elemento */ if ( elemento -> nombre ! = NULL ) { free ( elemento -> nombre ); elemento -> nombre = NULL ; }           /* Desasigna el objeto Item en sí */ free ( item ); } 

Hardware mapeado en memoria

En algunas arquitecturas informáticas, se pueden utilizar punteros para manipular directamente la memoria o los dispositivos mapeados en memoria.

Asignar direcciones a punteros es una herramienta invaluable a la hora de programar microcontroladores . A continuación se muestra un ejemplo sencillo que declara un puntero de tipo int e inicializa su dirección hexadecimal ; en este ejemplo, la constante 0x7FFF:

int * dirección_hardware = ( int * ) 0x7FFF ;    

A mediados de los 80, el uso del BIOS para acceder a las capacidades de vídeo de las PC era lento. Las aplicaciones con uso intensivo de pantalla generalmente se usaban para acceder a la memoria de video CGA directamente al convertir la constante hexadecimal 0xB8000 en un puntero a una matriz de 80 valores int de 16 bits sin signo. Cada valor constaba de un código ASCII en el byte bajo y un color en el byte alto. Por lo tanto, para poner la letra 'A' en la fila 5, columna 2 en blanco brillante sobre azul, se escribiría un código como el siguiente:

#define VID ((corto sin firmar (*)[80])0xB8000)vacío foo ( vacío ) { VID [ 4 ][ 1 ] = 0x1F00 | 'A' ; }       

Uso en mesas de control.

Las tablas de control que se utilizan para controlar el flujo del programa suelen hacer un uso extensivo de punteros. Los punteros, normalmente incrustados en una entrada de la tabla, pueden usarse, por ejemplo, para mantener los puntos de entrada a las subrutinas que se ejecutarán, basándose en ciertas condiciones definidas en la misma entrada de la tabla. Sin embargo, los punteros pueden ser simplemente índices de otras tablas separadas, pero asociadas, que comprenden una matriz de direcciones reales o las direcciones mismas (dependiendo de las construcciones del lenguaje de programación disponibles). También se pueden utilizar para señalar entradas anteriores de la tabla (como en el procesamiento de bucles) o reenviar para omitir algunas entradas de la tabla (como en un cambio o salida "anticipada" de un bucle). Para este último propósito, el "puntero" puede ser simplemente el número de entrada de la tabla y puede transformarse en una dirección real mediante simple aritmética.

Punteros mecanografiados y casting.

En muchos idiomas, los punteros tienen la restricción adicional de que el objeto al que apuntan tiene un tipo específico . Por ejemplo, se puede declarar que un puntero apunta a un número entero ; Luego, el lenguaje intentará evitar que el programador apunte a objetos que no sean números enteros, como números de punto flotante , eliminando algunos errores.

Por ejemplo, en C

int * dinero ; bolsas de carbón * ;  

moneysería un puntero entero y bagssería un puntero char. Lo siguiente generaría una advertencia del compilador de "asignación de tipo de puntero incompatible" en GCC

bolsas = dinero ;  

porque moneyy bagsfueron declarados con diferentes tipos. Para suprimir la advertencia del compilador, se debe dejar explícito que realmente desea realizar la asignación encasillándola .

bolsas = ( char * ) dinero ;   

que dice convertir el puntero entero de moneya un puntero char y asignarlo a bags.

Un borrador de 2005 del estándar C requiere que la conversión de un puntero derivado de un tipo a uno de otro tipo debe mantener la alineación correcta para ambos tipos (6.3.2.3 Punteros, par. 7): [9]

char * external_buffer = "abcdef" ; int * datos_internos ;    datos_internos = ( int * ) buffer_externo ; // COMPORTAMIENTO NO DEFINIDO si "el puntero resultante // no está correctamente alineado"     

En los lenguajes que permiten la aritmética de punteros, la aritmética de punteros tiene en cuenta el tamaño del tipo. Por ejemplo, agregar un número entero a un puntero produce otro puntero que apunta a una dirección que es mayor en ese número de veces el tamaño del tipo. Esto nos permite calcular fácilmente la dirección de los elementos de una matriz de un tipo determinado, como se muestra en el ejemplo de matrices C anterior. Cuando un puntero de un tipo se convierte a otro tipo de diferente tamaño, el programador debe esperar que la aritmética del puntero se calcule de manera diferente. En C, por ejemplo, si la moneymatriz comienza en 0x2000 y sizeof(int)tiene 4 bytes, mientras que sizeof(char)tiene 1 byte, entonces money + 1apuntará a 0x2004, pero bags + 1apuntará a 0x2001. Otros riesgos de la conversión incluyen la pérdida de datos cuando se escriben datos "amplios" en ubicaciones "estrechas" (p. ej. bags[0] = 65537;), resultados inesperados al cambiar valores de bits y problemas de comparación, especialmente con valores con signo y sin signo.

Aunque en general es imposible determinar en tiempo de compilación qué conversiones son seguras, algunos lenguajes almacenan información de tipo en tiempo de ejecución que puede usarse para confirmar que estas conversiones peligrosas son válidas en tiempo de ejecución. Otros lenguajes simplemente aceptan una aproximación conservadora de castas seguras, o ninguna en absoluto.

Valor de los punteros

En C y C++, incluso si dos punteros se comparan como iguales, eso no significa que sean equivalentes. En estos lenguajes y LLVM , la regla se interpreta en el sentido de que "el hecho de que dos punteros apunten a la misma dirección no significa que sean iguales en el sentido de que puedan usarse indistintamente", la diferencia entre los punteros se denomina su procedencia . [10] La conversión a un tipo entero como uintptr_tel que está definido por la implementación y la comparación que proporciona no proporciona más información sobre si los dos punteros son intercambiables. Además, una mayor conversión a bytes y aritmética desanimará a los optimizadores que intentan realizar un seguimiento del uso de punteros, un problema que aún se está dilucidando en la investigación académica. [11]

Hacer que los punteros sean más seguros

Como un puntero permite a un programa intentar acceder a un objeto que puede no estar definido, los punteros pueden ser el origen de una variedad de errores de programación . Sin embargo, la utilidad de los punteros es tan grande que puede resultar difícil realizar tareas de programación sin ellos. En consecuencia, muchos lenguajes han creado construcciones diseñadas para proporcionar algunas de las características útiles de los punteros sin algunos de sus inconvenientes , también denominados a veces peligros de puntero . En este contexto, los punteros que se dirigen directamente a la memoria (como se utilizan en este artículo) se denominanpunteros sin formato , a diferencia delos punteros inteligentesu otras variantes.

Un problema importante con los punteros es que, siempre que puedan manipularse directamente como un número, se puede hacer que apunten a direcciones no utilizadas o a datos que se utilizan para otros fines. Muchos lenguajes, incluidos la mayoría de los lenguajes de programación funcionales y los lenguajes imperativos recientes como Java , reemplazan los punteros con un tipo de referencia más opaco, generalmente denominado simplemente referencia , que solo puede usarse para referirse a objetos y no manipularse como números, lo que evita que esto suceda. tipo de error. La indexación de matrices se trata como un caso especial.

Un puntero que no tiene ninguna dirección asignada se llama puntero salvaje . Cualquier intento de utilizar dichos punteros no inicializados puede provocar un comportamiento inesperado, ya sea porque el valor inicial no es una dirección válida o porque su uso puede dañar otras partes del programa. El resultado suele ser un error de segmentación , una infracción de almacenamiento o una rama salvaje (si se utiliza como puntero de función o dirección de rama).

En sistemas con asignación de memoria explícita, es posible crear un puntero colgante desasignando la región de memoria a la que apunta. Este tipo de puntero es peligroso y sutil porque una región de memoria desasignada puede contener los mismos datos que tenía antes de ser desasignada, pero luego puede ser reasignada y sobrescrita por código no relacionado, desconocido para el código anterior. Los lenguajes con recolección de basura evitan este tipo de error porque la desasignación se realiza automáticamente cuando no hay más referencias en el alcance.

Algunos lenguajes, como C++ , admiten punteros inteligentes , que utilizan una forma sencilla de recuento de referencias para ayudar a realizar un seguimiento de la asignación de memoria dinámica además de actuar como referencia. En ausencia de ciclos de referencia, donde un objeto se refiere a sí mismo indirectamente a través de una secuencia de punteros inteligentes, estos eliminan la posibilidad de punteros colgantes y pérdidas de memoria. Las cadenas de Delphi admiten el recuento de referencias de forma nativa.

El lenguaje de programación Rust introduce un verificador de préstamos , duración de los punteros y una optimización basada en tipos de opciones para punteros nulos para eliminar errores de punteros, sin recurrir a la recolección de basura .

Tipos especiales de punteros.

Tipos definidos por valor

Puntero nulo

Un puntero nulo tiene un valor reservado para indicar que el puntero no hace referencia a un objeto válido. Los punteros nulos se utilizan habitualmente para representar condiciones como el final de una lista de longitud desconocida o la imposibilidad de realizar alguna acción; Este uso de punteros nulos se puede comparar con tipos que aceptan valores NULL y con el valor Nothing en un tipo de opción .

puntero colgando

Un puntero colgante es un puntero que no apunta a un objeto válido y, en consecuencia, puede provocar que un programa falle o se comporte de manera extraña. En los lenguajes de programación Pascal o C , los punteros que no se inicializan específicamente pueden apuntar a direcciones impredecibles en la memoria.

El siguiente código de ejemplo muestra un puntero colgante:

int func ( void ) { char * p1 = malloc ( sizeof ( char )); /* valor (indefinido) de algún lugar en el montón */ char * p2 ; /* puntero colgante (no inicializado) */ * p1 = 'a' ; /* Esto está bien, suponiendo que malloc() no haya devuelto NULL. */ * p2 = 'b' ; /* Esto invoca un comportamiento indefinido */ }                  

Aquí, p2puede apuntar a cualquier lugar de la memoria, por lo que realizar la asignación *p2 = 'b';puede dañar un área desconocida de la memoria o desencadenar una falla de segmentación .

rama salvaje

Cuando se utiliza un puntero como dirección del punto de entrada a un programa o inicio de una función que no devuelve nada y además no está inicializado o está dañado, si de todos modos se realiza una llamada o un salto a esta dirección, se genera una " rama salvaje" . "Se dice que ocurrió. En otras palabras, una rama salvaje es un puntero de función salvaje (colgante).

Las consecuencias suelen ser impredecibles y el error puede presentarse de varias maneras diferentes dependiendo de si el puntero es una dirección "válida" o no y de si hay o no (casualmente) una instrucción válida (código de operación) en esa dirección. La detección de una rama salvaje puede presentar uno de los ejercicios de depuración más difíciles y frustrantes, ya que es posible que gran parte de la evidencia ya haya sido destruida de antemano o mediante la ejecución de una o más instrucciones inapropiadas en la ubicación de la rama. Si está disponible, un simulador de conjunto de instrucciones normalmente no sólo puede detectar una rama salvaje antes de que entre en vigor, sino que también proporciona un rastro completo o parcial de su historial.

Tipos definidos por estructura

puntero autorrelativo

Un puntero autorelativo es un puntero cuyo valor se interpreta como un desplazamiento de la dirección del propio puntero; por lo tanto, si una estructura de datos tiene un miembro puntero autorelativo que apunta a alguna parte de la propia estructura de datos, entonces la estructura de datos puede reubicarse en la memoria sin tener que actualizar el valor del puntero automático relativo. [12]

La patente citada también utiliza el término puntero relativo a sí mismo para significar lo mismo. Sin embargo, el significado de ese término se ha utilizado de otras maneras:

puntero basado

Un puntero basado es un puntero cuyo valor es un desplazamiento del valor de otro puntero. Esto se puede utilizar para almacenar y cargar bloques de datos, asignando la dirección del inicio del bloque al puntero base. [14]

Tipos definidos por uso o tipo de datos

Indirección múltiple

En algunos idiomas, un puntero puede hacer referencia a otro puntero, lo que requiere múltiples operaciones de desreferenciación para llegar al valor original. Si bien cada nivel de direccionamiento indirecto puede agregar un costo de rendimiento, a veces es necesario para proporcionar un comportamiento correcto para estructuras de datos complejas . Por ejemplo, en C es típico definir una lista enlazada en términos de un elemento que contiene un puntero al siguiente elemento de la lista:

elemento de estructura { elemento de estructura * siguiente ; valor entero ; };       elemento de estructura * cabeza = NULL ;    

Esta implementación utiliza un puntero al primer elemento de la lista como sustituto de toda la lista. Si se agrega un nuevo valor al principio de la lista, headdebe cambiarse para que apunte al nuevo elemento. Dado que los argumentos de C siempre se pasan por valor, el uso de la doble dirección indirecta permite que la inserción se implemente correctamente y tiene el efecto secundario deseable de eliminar el código de casos especiales para tratar las inserciones al principio de la lista:

// Dada una lista ordenada en *head, inserte el elemento item en la primera // ubicación donde todos los elementos anteriores tienen menor o igual valor. inserción vacía ( elemento de estructura ** cabeza , elemento de estructura * elemento ) { elemento de estructura ** p ; // p apunta a un puntero a un elemento for ( p = cabeza ; * p ! = NULL ; p = & ( * p ) -> siguiente ) { if ( elemento -> valor <= ( * p ) -> valor ) romper ; } elemento -> siguiente = * p ; * p = ítem ; }                                  // La persona que llama hace esto: insert ( & head , item ); 

En este caso, si el valor de itemes menor que el de head, el de la persona que llama headse actualiza correctamente a la dirección del nuevo elemento.

Un ejemplo básico está en el argumento argv de la función principal en C (y C++) , que se proporciona en el prototipo como char **argv: esto se debe a que la variable argven sí es un puntero a una matriz de cadenas (una matriz de matrices), también lo *argves un puntero a la cadena 0 (por convención, el nombre del programa) y **argves el carácter 0 de la cadena 0.

Puntero de función

En algunos lenguajes, un puntero puede hacer referencia a código ejecutable, es decir, puede apuntar a una función, método o procedimiento. Un puntero de función almacenará la dirección de una función que se va a invocar. Si bien esta función se puede utilizar para llamar funciones dinámicamente, suele ser una técnica favorita de los creadores de virus y otros programas maliciosos.

int sum ( int n1 , int n2 ) { // Función con dos parámetros enteros que devuelven un valor entero return n1 + n2 ; }          int principal ( vacío ) { int a , b , x , y ; int ( * fp )( int , int ); // Puntero de función que puede apuntar a una función como sum fp = & sum ; // fp ahora apunta a la función suma x = ( * fp )( a , b ); // Llama a la función suma con argumentos a y b y = suma ( a , b ); // Llama a la función suma con argumentos a y b }                         

puntero hacia atrás

En listas doblemente enlazadas o estructuras de árbol , un puntero hacia atrás sostenido sobre un elemento "apunta hacia atrás" al elemento que hace referencia al elemento actual. Son útiles para la navegación y la manipulación, a expensas de un mayor uso de memoria.

Simulación utilizando un índice de matriz.

Es posible simular el comportamiento del puntero utilizando un índice de una matriz (normalmente unidimensional).

Principalmente para los lenguajes que no admiten punteros explícitamente pero admiten matrices, la matriz se puede considerar y procesar como si fuera todo el rango de memoria (dentro del alcance de la matriz en particular) y cualquier índice de ella se puede considerar como equivalente. a un registro de propósito general en lenguaje ensamblador (que apunta a los bytes individuales pero cuyo valor real es relativo al inicio de la matriz, no a su dirección absoluta en la memoria). Suponiendo que la matriz es, digamos, una estructura de datos de caracteres contiguos de 16 megabytes , los bytes individuales (o una cadena de bytes contiguos dentro de la matriz) se pueden direccionar y manipular directamente utilizando el nombre de la matriz con un entero sin signo de 31 bits como puntero simulado. (Esto es bastante similar al ejemplo de matrices C que se muestra arriba). La aritmética de punteros se puede simular sumando o restando del índice, con una sobrecarga adicional mínima en comparación con la aritmética de punteros genuina.

Incluso es teóricamente posible, utilizando la técnica anterior, junto con un simulador de conjunto de instrucciones adecuado, simular cualquier código de máquina o el intermedio ( código de bytes ) de cualquier procesador/lenguaje en otro lenguaje que no admita punteros en absoluto (por ejemplo, Java / JavaScript ). Para lograr esto, el código binario se puede cargar inicialmente en bytes contiguos de la matriz para que el simulador lo "lea", interprete y actúe completamente dentro de la memoria contenida en la misma matriz. Si es necesario, para evitar por completo problemas de desbordamiento del búfer , la verificación de límites generalmente se puede realizar para el compilador (o, si no, codificarse manualmente en el simulador).

Soporte en varios lenguajes de programación.

ada

Ada es un lenguaje fuertemente tipado donde todos los punteros se escriben y solo se permiten conversiones de tipos seguras. Todos los punteros se inicializan de forma predeterminada en nully cualquier intento de acceder a los datos a través de un nullpuntero provoca que se genere una excepción . Los punteros en Ada se denominan tipos de acceso . Ada 83 no permitía aritmética en los tipos de acceso (aunque muchos proveedores de compiladores la proporcionaban como una característica no estándar), pero Ada 95 admite aritmética "segura" en los tipos de acceso a través del paquete System.Storage_Elements.

BÁSICO

Varias versiones antiguas de BASIC para la plataforma Windows admitían STRPTR() para devolver la dirección de una cadena y VARPTR() para devolver la dirección de una variable. Visual Basic 5 también admitía que OBJPTR() devolviera la dirección de una interfaz de objeto y que un operador ADDRESSOF devolviera la dirección de una función. Los tipos de todos estos son números enteros, pero sus valores son equivalentes a los de los tipos de puntero.

Sin embargo , los dialectos más nuevos de BASIC , como FreeBASIC o BlitzMax , tienen implementaciones de puntero exhaustivas. En FreeBASIC, la aritmética de ANYlos punteros (equivalentes a C void*) se trata como si el ANYpuntero tuviera un ancho de byte. ANYNo se puede desreferenciar los punteros, como en C. Además, la conversión entre ANYpunteros y de cualquier otro tipo no generará ninguna advertencia.

tenue como entero f = 257 tenue como cualquier ptr g = @ f tenue como entero ptr i = g afirmar ( * i = 257 ) afirmar ( ( g + 4 ) = ( @ f + 1 ) )                           

C y C++

En C y C++ los punteros son variables que almacenan direcciones y pueden ser nulos . Cada puntero tiene un tipo al que apunta, pero se puede convertir libremente entre tipos de puntero (pero no entre un puntero de función y un puntero de objeto). Un tipo de puntero especial llamado “puntero vacío” permite apuntar a cualquier objeto (que no sea funcional), pero está limitado por el hecho de que no se puede desreferenciar directamente (deberá convertirse). La dirección misma a menudo puede manipularse directamente lanzando un puntero hacia y desde un tipo integral de tamaño suficiente, aunque los resultados están definidos por la implementación y, de hecho, pueden causar un comportamiento indefinido; Si bien los estándares C anteriores no tenían un tipo integral que garantizara que fuera lo suficientemente grande, C99 especifica el nombre uintptr_t de typedef definido en <stdint.h>, pero no es necesario que una implementación lo proporcione.

C++ es totalmente compatible con los punteros C y el encasillamiento en C. También admite un nuevo grupo de operadores de encasillamiento para ayudar a detectar algunas conversiones peligrosas no deseadas en tiempo de compilación. Desde C++ 11 , la biblioteca estándar de C++ también proporciona punteros inteligentes ( unique_ptr, shared_ptry weak_ptr) que pueden usarse en algunas situaciones como una alternativa más segura a los punteros C primitivos. C++ también admite otra forma de referencia, bastante diferente de un puntero, llamada simplemente referencia o tipo de referencia .

La aritmética de punteros , es decir, la capacidad de modificar la dirección de destino de un puntero con operaciones aritméticas (así como comparaciones de magnitudes), está restringida por el estándar del lenguaje para permanecer dentro de los límites de un único objeto de matriz (o justo después de él), y de lo contrario, invocará un comportamiento indefinido . Sumar o restar de un puntero lo mueve en un múltiplo del tamaño de su tipo de datos . Por ejemplo, agregar 1 a un puntero a valores enteros de 4 bytes incrementará la dirección de byte apuntada del puntero en 4. Esto tiene el efecto de incrementar el puntero para que apunte al siguiente elemento en una matriz contigua de números enteros, que es a menudo el resultado deseado. La aritmética de punteros no se puede realizar en voidpunteros porque el tipo void no tiene tamaño y, por lo tanto, no se puede agregar la dirección apuntada, aunque gcc y otros compiladores realizarán aritmética de bytes void*como una extensión no estándar, tratándola como si fuera char *.

La aritmética de punteros proporciona al programador una forma única de tratar con diferentes tipos: sumar y restar el número de elementos necesarios en lugar del desplazamiento real en bytes. (La aritmética de punteros con char *punteros utiliza desplazamientos de bytes, porque sizeof(char)es 1 por definición). En particular, la definición de C declara explícitamente que la sintaxis a[n], que es el n-ésimo elemento de la matriz a, es equivalente a *(a + n), que es el contenido del elemento señalado. por a + n. Esto implica que n[a]es equivalente a a[n]y se puede escribir, por ejemplo, a[3]o 3[a]igualmente bien para acceder al cuarto elemento de una matriz a.

Si bien es poderosa, la aritmética de punteros puede ser una fuente de errores informáticos . Tiende a confundir a los programadores novatos , obligándolos a entrar en contextos diferentes: una expresión puede ser aritmética ordinaria o aritmética de punteros, y a veces es fácil confundir una con la otra. En respuesta a esto, muchos lenguajes informáticos modernos de alto nivel (por ejemplo, Java ) no permiten el acceso directo a la memoria mediante direcciones. Además, el dialecto C seguro Cyclone soluciona muchos de los problemas con los punteros. Consulte el lenguaje de programación C para obtener más información.

El voidpuntero , o void*, se admite en ANSI C y C++ como tipo de puntero genérico. Un puntero a voidpuede almacenar la dirección de cualquier objeto (no función), [a] y, en C, se convierte implícitamente a cualquier otro tipo de puntero de objeto en la asignación, pero debe convertirse explícitamente si se elimina la referencia. K&R C se utilizaba char*para el propósito de “puntero independiente del tipo” (antes de ANSI C).

int x = 4 ; vacío * p1 = & x ; entero * p2 = p1 ; // void* implícitamente convertido a int*: C válido, pero no C++ int a = * p2 ; int b = * ( int * ) p1 ; // cuando se elimina la referencia en línea, no hay conversión implícita                 

C++ no permite la conversión implícita void*a otros tipos de puntero, ni siquiera en asignaciones. Esta fue una decisión de diseño para evitar conversiones descuidadas e incluso no intencionadas, aunque la mayoría de los compiladores solo generan advertencias, no errores, cuando encuentran otras conversiones.

int x = 4 ; vacío * p1 = & x ; entero * p2 = p1 ; // esto falla en C++: no hay conversión implícita de void* int * p3 = ( int * ) p1 ; // conversión estilo C int * p4 = reinterpret_cast < int *> ( p1 ); // conversión C++                  

En C++, no hay void&(referencia a void) para complementar void*(puntero a void), porque las referencias se comportan como alias de las variables a las que apuntan, y nunca puede haber una variable cuyo tipo sea void.

Puntero a miembro

En C++ se pueden definir punteros a miembros no estáticos de una clase. Si una clase Ctiene un miembro, T aentonces &C::aes un puntero al miembro ade tipo T C::*. Este miembro puede ser un objeto o una función . [16] Se pueden utilizar en el lado derecho de los operadores .*y ->*para acceder al miembro correspondiente.

estructura S { int a ; int f () const { return a ;} }; S s1 {}; S * ptrS = & s1 ;           int S ::* ptr = & S :: a ; // puntero a S::a int ( S ::* fp )() const = & S :: f ; // puntero a S::f          s1 . * ptr = 1 ; std :: cout << ( s1 . * fp )() << " \n " ; // imprime 1 ptrS ->* ptr = 2 ; std :: cout << ( ptrS ->* fp )() << " \n " ; // imprime 2              

Descripción general de la sintaxis de declaración de puntero

Estas declaraciones de puntero cubren la mayoría de las variantes de declaraciones de puntero. Por supuesto, es posible tener punteros triples, pero los principios fundamentales detrás de un puntero triple ya existen en un puntero doble. El nombre utilizado aquí es lo que equivale a la expresión typeid(type).name()para cada uno de estos tipos cuando se usa g++ o clang . [17] [18]

carácter A5_A5_c [ 5 ][ 5 ]; /* matriz de matrices de caracteres */ char * A5_Pc [ 5 ]; /* matriz de punteros a caracteres */ char ** PPc ; /* puntero a puntero a carácter ("puntero doble") */ char ( * PA5_c ) [ 5 ]; /* puntero a matriz(s) de caracteres */ char * FPcvE (); /* función que devuelve un puntero a char(s) */ char ( * PFcvE )(); /* puntero a una función que devuelve un carácter */ char ( * FPA5_cvE ())[ 5 ]; /* función que devuelve un puntero a una matriz de caracteres */ char ( * A5_PFcvE [ 5 ])(); /* una serie de punteros a funciones que devuelven un carácter */                   

Las siguientes declaraciones que involucran punteros a miembro sólo son válidas en C++:

clase C ; clase D ; carácter C ::* M1Cc ; /* puntero a miembro a char */ char C ::* A5_M1Cc [ 5 ]; /* matriz de punteros a miembro de char */ char * C ::* M1CPc ; /* puntero a miembro a puntero a caracteres */ char C ::** PM1Cc ; /* puntero a puntero a miembro a char */ char ( * M1CA5_c ) [ 5 ]; /* puntero a miembro a matriz(s) de caracteres */ char C ::* FM1CcvE (); /* función que devuelve un puntero a miembro a char */ char D ::* C ::* M1CM1Dc ; /* puntero a miembro a puntero a miembro a puntero a caracteres */ char C ::* C ::* M1CMS_c ; /* puntero a miembro a puntero a miembro a puntero a caracteres */ char ( C ::* FM1CA5_cvE ())[ 5 ]; /* función que devuelve puntero a miembro a una matriz de caracteres */ char ( C ::* M1CFcvE )() /* función puntero a miembro que devuelve un carácter */ char ( C :: * A5_M1CFcvE [ 5 ])(); /* una serie de punteros a funciones miembro que devuelven un carácter */                                     

Los ()y []tienen mayor prioridad que *.[19]

C#

En el lenguaje de programación C# , los punteros se admiten mediante bloques de código de marcado que incluyen punteros con la unsafepalabra clave o mediante usingdisposiciones System.Runtime.CompilerServicesde ensamblaje para el acceso al puntero. La sintaxis es esencialmente la misma que en C++ y la dirección señalada puede ser memoria administrada o no administrada . Sin embargo, los punteros a la memoria administrada (cualquier puntero a un objeto administrado) deben declararse usando la fixedpalabra clave, lo que evita que el recolector de basura mueva el objeto señalado como parte de la administración de la memoria mientras el puntero está dentro del alcance, manteniendo así la dirección del puntero válida.

Sin embargo, una excepción a esto es el uso de la IntPtrestructura, que es una memoria administrada equivalente a int*y no requiere la unsafepalabra clave ni el CompilerServicesensamblado. Este tipo suele devolverse cuando se utilizan métodos de System.Runtime.InteropServices, por ejemplo:

// Obtenga 16 bytes de memoria de la memoria no administrada del proceso IntPtr pointer = System . Tiempo de ejecución . Servicios de interoperabilidad . Mariscal . AllocHGlobal ( 16 );   //Hacer algo con la memoria asignada// Libera la memoria asignada System . Tiempo de ejecución . Servicios de interoperabilidad . Mariscal . FreeHGlobal ( puntero );

El marco .NET incluye muchas clases y métodos en los espacios de nombres Systemy System.Runtime.InteropServices(como la Marshalclase) que convierten tipos .NET (por ejemplo, System.String) hacia y desde muchos tipos y punteros no administradosLPWSTR (por ejemplo, o void*) para permitir la comunicación con código no administrado . La mayoría de estos métodos tienen los mismos requisitos de permisos de seguridad que el código no administrado, ya que pueden afectar lugares arbitrarios en la memoria.

COBOL

El lenguaje de programación COBOL admite punteros a variables. Los objetos de datos primitivos o de grupo (registro) declarados dentro LINKAGE SECTIONde un programa están inherentemente basados ​​en punteros, donde la única memoria asignada dentro del programa es espacio para la dirección del elemento de datos (normalmente una sola palabra de memoria). En el código fuente del programa, estos elementos de datos se utilizan como cualquier otra WORKING-STORAGEvariable, pero se accede implícitamente a sus contenidos de forma indirecta a través de sus LINKAGEpunteros.

El espacio de memoria para cada objeto de datos al que se apunta generalmente se asigna dinámicamente mediante declaraciones externas CALLo mediante construcciones de lenguaje extendido integradas, como declaraciones EXEC CICSo EXEC SQL.

Las versiones extendidas de COBOL también proporcionan variables de puntero declaradas con USAGE IS POINTERcláusulas. Los valores de dichas variables indicadoras se establecen y modifican mediante declaraciones SETy SET ADDRESS.

Algunas versiones extendidas de COBOL también proporcionan PROCEDURE-POINTERvariables, que son capaces de almacenar las direcciones de código ejecutable .

PL/I

El lenguaje PL/I proporciona soporte completo para punteros a todos los tipos de datos (incluidos punteros a estructuras), recursividad , multitarea , manejo de cadenas y amplias funciones integradas . PL/I supuso un gran avance en comparación con los lenguajes de programación de su época. [ cita necesaria ] Los punteros PL/I no están tipificados y, por lo tanto, no se requiere conversión para la desreferenciación o asignación de punteros. La sintaxis de declaración de un puntero es DECLARE xxx POINTER;, que declara un puntero denominado "xxx". Los punteros se utilizan con BASEDvariables. Una variable basada se puede declarar con un localizador predeterminado ( DECLARE xxx BASED(ppp);o sin ( DECLARE xxx BASED;), donde xxx es una variable basada, que puede ser una variable de elemento, una estructura o una matriz, y ppp es el puntero predeterminado). Dicha variable puede ser una dirección sin una referencia de puntero explícita ( xxx=1;, o puede ser direccionada con una referencia explícita al localizador predeterminado (ppp) o a cualquier otro puntero ( qqq->xxx=1;).

La aritmética de punteros no forma parte del estándar PL/I, pero muchos compiladores permiten expresiones de la forma ptr = ptr±expression. IBM PL/I también tiene la función incorporada PTRADDpara realizar la aritmética. La aritmética de punteros siempre se realiza en bytes.

Los compiladores IBM Enterprise PL/I tienen una nueva forma de puntero escrito llamado HANDLE.

D

El lenguaje de programación D es un derivado de C y C++ que admite totalmente los punteros de C y el encasillamiento de C.

eiffel

El lenguaje orientado a objetos de Eiffel emplea semántica de valores y referencias sin aritmética de punteros. Sin embargo, se proporcionan clases de puntero. Ofrecen aritmética de punteros, encasillamiento, gestión de memoria explícita, interfaz con software que no es de Eiffel y otras características.

fortran

Fortran-90 introdujo una capacidad de puntero fuertemente tipado. Los punteros de Fortran contienen más que una simple dirección de memoria. También encapsulan los límites inferior y superior de las dimensiones de la matriz, los pasos (por ejemplo, para admitir secciones arbitrarias de la matriz) y otros metadatos. Un operador de asociación se =>utiliza para asociar a POINTERuna variable que tiene un TARGETatributo. La declaración Fortran-90 ALLOCATEtambién se puede utilizar para asociar un puntero a un bloque de memoria. Por ejemplo, el siguiente código podría usarse para definir y crear una estructura de lista vinculada:

tipo lista_real_t real :: datos_muestra ( 100 ) tipo ( lista_real_t ), puntero :: siguiente => nulo () tipo final           tipo ( lista_real_t ), destino :: mi_lista_real tipo ( lista_real_t ), puntero :: lista_real_temp        real_list_temp => my_real_list leer ( 1 , iostat = ioerr ) real_list_temp % sample_data if ( ioerr /= 0 ) salir  asignar ( real_list_temp % siguiente ) real_list_temp => real_list_temp % siguiente  fin hacer             

Fortran-2003 agrega soporte para punteros de procedimientos. Además, como parte de la función de interoperabilidad de C , Fortran-2003 admite funciones intrínsecas para convertir punteros de estilo C en punteros de Fortran y viceversa.

Ir

Go tiene sugerencias. Su sintaxis de declaración es equivalente a la de C, pero escrita al revés, terminando con el tipo. A diferencia de C, Go tiene recolección de basura y no permite la aritmética de punteros. Los tipos de referencia, como en C++, no existen. Algunos tipos integrados, como mapas y canales, están encuadrados (es decir, internamente son punteros a estructuras mutables) y se inicializan mediante la makefunción. En una aproximación a la sintaxis unificada entre punteros y no punteros, ->se ha eliminado el operador de flecha ( ): el operador de punto en un puntero se refiere al campo o método del objeto desreferenciado. Sin embargo, esto sólo funciona con 1 nivel de direccionamiento indirecto.

Java

No existe una representación explícita de punteros en Java . En cambio, se implementan estructuras de datos más complejas, como objetos y matrices, mediante referencias . El lenguaje no proporciona ningún operador explícito de manipulación de punteros. Sin embargo, aún es posible que el código intente eliminar la referencia a una referencia nula (puntero nulo), lo que genera una excepción en tiempo de ejecución . El espacio ocupado por objetos de memoria sin referencia se recupera automáticamente mediante la recolección de basura en tiempo de ejecución. [20]

Módulo-2

Los punteros se implementan de manera muy similar a Pascal, al igual que VARlos parámetros en las llamadas a procedimientos. Modula-2 tiene un tipo aún más fuerte que Pascal, con menos formas de escapar del sistema de tipos. Algunas de las variantes de Modula-2 (como Modula-3 ) incluyen recolección de basura.

Oberón

Al igual que con Modula-2, hay punteros disponibles. Todavía hay menos formas de evadir el sistema de tipos, por lo que Oberon y sus variantes siguen siendo más seguros con respecto a los punteros que Modula-2 o sus variantes. Al igual que con Modula-3 , la recolección de basura es parte de la especificación del lenguaje.

Pascal

A diferencia de muchos lenguajes que cuentan con punteros, el estándar ISO Pascal solo permite que los punteros hagan referencia a variables creadas dinámicamente que son anónimas y no les permite hacer referencia a variables locales o estáticas estándar. [21] No tiene aritmética de punteros. Los punteros también deben tener un tipo asociado y un puntero a un tipo no es compatible con un puntero a otro tipo (por ejemplo, un puntero a un carácter no es compatible con un puntero a un número entero). Esto ayuda a eliminar los problemas de seguridad de tipos inherentes a otras implementaciones de punteros, en particular las utilizadas para PL /I o C. También elimina algunos riesgos causados ​​por punteros colgantes , pero la capacidad de liberar dinámicamente el espacio referenciado mediante el disposeprocedimiento estándar (que tiene el mismo efecto que la freefunción de biblioteca que se encuentra en C ) significa que el riesgo de punteros colgantes no ha sido completamente eliminado. eliminado. [22]

Sin embargo, en algunas implementaciones del compilador Pascal (o derivados) comerciales y de código abierto, como Free Pascal , [23] Turbo Pascal o el Object Pascal en Embarcadero Delphi , se permite que un puntero haga referencia a variables locales o estáticas estándar y se puede convertir desde una tipo de puntero a otro. Además, la aritmética de punteros no tiene restricciones: sumar o restar a un puntero lo mueve ese número de bytes en cualquier dirección, pero al utilizar los procedimientos estándar Inco Decse mueve el puntero según el tamaño del tipo de datos al que se declara que apunta. También se proporciona un puntero sin tipo bajo el nombre Pointer, que es compatible con otros tipos de puntero.

perla

El lenguaje de programación Perl admite punteros, aunque rara vez se utilizan, en forma de funciones de empaquetar y desempaquetar. Están destinados únicamente a interacciones simples con bibliotecas de sistemas operativos compiladas. En todos los demás casos, Perl utiliza referencias , que están escritas y no permiten ningún tipo de aritmética de punteros. Se utilizan para construir estructuras de datos complejas. [24]

Ver también

Notas

  1. ^ Algunos compiladores permiten almacenar las direcciones de funciones en punteros vacíos. El estándar C++ enumera la conversión de un puntero a función void*como una característica admitida condicionalmente y el estándar C dice que dichas conversiones son "extensiones comunes". Esto es requerido por la función POSIXdlsym . [15]

Referencias

  1. ^ Donald Knuth (1974). "Programación estructurada con declaraciones go to" (PDF) . Encuestas Informáticas . 6 (5): 261–301. CiteSeerX  10.1.1.103.6084 . doi :10.1145/356635.356640. S2CID  207630080. Archivado desde el original (PDF) el 24 de agosto de 2009.
  2. ^ Reilly, Edwin D. (2003). Hitos en Informática y Tecnologías de la Información . Grupo editorial Greenwood. pag. 204.ISBN _ 9781573565219. Consultado el 13 de abril de 2018 . Puntero de Harold Lawson.
  3. ^ "Lista de premios de la IEEE Computer Society". Premios.computer.org. Archivado desde el original el 22 de marzo de 2011 . Consultado el 13 de abril de 2018 .
  4. ^ ISO/IEC 9899, ​​cláusula 6.7.5.1, párrafo 1.
  5. ^ ISO/IEC 9899, ​​cláusula 6.7.8, párrafo 10.
  6. ^ 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...
  7. ^ ISO/IEC 9899, ​​cláusula 6.5.3.2, párrafo 4, nota al pie 87: si se ha asignado un valor no válido al puntero, el comportamiento del operador unario * no está definido... Entre los valores no válidos para desreferenciar un puntero por parte del El operador unario * es un puntero nulo...
  8. ^ ab Plauger, PJ ; Brodie, Jim (1992). Referencia del programador ANSI e ISO Standard C. Redmond, WA: Microsoft Press. págs.108, 51. ISBN 978-1-55615-359-4. Un tipo de matriz no contiene orificios adicionales porque todos los demás tipos se empaquetan estrechamente cuando se componen en matrices [en la página 51]
  9. ^ WG14 N1124, C - Estándares aprobados: ISO/IEC 9899 - Lenguajes de programación - C, 2005-05-06.
  10. ^ Jung, Ralf. "Los punteros son complicados II, o: Necesitamos mejores especificaciones de lenguaje".
  11. ^ Jung, Ralf. "Los punteros son complicados o: ¿Qué hay en un byte?".
  12. ^ patente estadounidense 6625718, Steiner, Robert C. (Broomfield, CO), "Indicadores relativos a sus ubicaciones actuales", emitido el 23 de septiembre de 2003, asignado a Avaya Technology Corp. (Basking Ridge, Nueva Jersey) 
  13. ^ patente estadounidense 6115721, Nagy, Michael (Tampa, FL), "Sistema y método para guardar y restaurar bases de datos mediante autopunteros", publicada el 5 de septiembre de 2000, asignada a IBM (Armonk, NY) 
  14. ^ "Consejos basados". msdn.microsoft.com . Consultado el 13 de abril de 2018 .
  15. ^ "Número 195 del CWG". cplusplus.github.io . Consultado el 15 de febrero de 2024 .
  16. ^ "Consejos para las funciones de los miembros". isocpp.org . Consultado el 26 de noviembre de 2022 .
  17. ^ "c++filt(1) - página de manual de Linux".
  18. ^ "ABI de Itanium C ++".
  19. ^ Ulf Bilting, Jan Skansholm, "Vägen hasta C" (el camino hacia C), tercera edición, página 169, ISBN 91-44-01468-6 
  20. ^ Nick Parlante, [1], Biblioteca de Educación en Ciencias de la Computación de Stanford, págs. 9-10 (2000).
  21. ^ Estándar Pascal ISO 7185 (copia no oficial), sección 6.4.4 Tipos de puntero Archivado el 24 de abril de 2017 en Wayback Machine y posteriores.
  22. ^ J. Welsh, WJ Sneeringer y CAR Hoare, "Ambigüedades e inseguridades en Pascal", Software: práctica y experiencia 7 , págs. 685–696 (1977)
  23. ^ Guía gratuita de referencia del lenguaje Pascal, sección 3.4 Consejos
  24. ^ Datos de contacto. "// Hacer referencias (referencias de Perl y estructuras de datos anidadas)". Perldoc.perl.org . Consultado el 13 de abril de 2018 .

enlaces externos