robot de la enciclopedia para niños

Puntero (informática) para niños

Enciclopedia para niños
Considero que las sentencias de asignación y variables puntero están entre los tesoros más valiosos de la informática
Donald Knuth, Structured Programming with go to Statements

En la ciencia de la computación, un puntero es como una nota especial en un programa de computadora. Su valor no es un dato en sí, sino que te dice dónde encontrar otro dato en la memoria de la computadora. Imagina que tienes un libro muy grande y en el índice hay un número de página. Ese número de página es como un puntero: te indica dónde está la información que buscas. Cuando vas a esa página para leer la información, a eso se le llama "desreferenciar" el puntero.

Archivo:Pointers
Puntero 'a' apuntando a la dirección de memoria asociada con la variable 'b'. Obsérvese que en este diagrama en particular, la arquitectura de cálculo utiliza el mismo espacio de direcciones y datos primitivos tanto para punteros como para no punteros; esto no tiene por qué ser el caso.

Los punteros son muy útiles porque ayudan a que los programas funcionen más rápido. Por ejemplo, al trabajar con listas o tablas grandes, es más eficiente copiar y usar punteros que copiar los datos completos. También se usan para que diferentes partes de un programa puedan comunicarse y compartir información.

Un puntero es una forma más directa de usar una "referencia", que es una idea más general de apuntar a algo. Algunos lenguajes de programación usan punteros de forma muy abierta, permitiendo a los programadores manipular directamente las direcciones de memoria. Esto puede ser muy potente, pero también puede causar errores si no se usan con cuidado. Si un puntero apunta a un lugar de la memoria que no es válido, el programa podría detenerse. Por eso, muchos lenguajes tienen reglas para hacer los punteros más seguros.

Historia de los Punteros

¿Quién inventó los punteros?

Se cree que Harold Lawson inventó el concepto de puntero en 1964. En el año 2000, Lawson recibió un premio importante por su invención, que permitió manejar listas de datos de forma flexible en lenguajes de programación avanzados. La palabra "puntero" apareció por primera vez en un documento técnico de la System Development Corporation.

¿Cómo funcionan los punteros?

Punteros y la memoria de la computadora

En la ciencia de la computación, un puntero es un tipo de "referencia". Un "dato primitivo" es la información más pequeña que la computadora puede leer o escribir en su memoria, como un solo byte. Un "conjunto de datos" es un grupo de datos primitivos que están juntos en la memoria y se tratan como una sola cosa.

Un puntero de memoria es un valor que se usa como una dirección de memoria. Se dice que un puntero "apunta" a una dirección de memoria. También se dice que "apunta a un dato" cuando su valor es la dirección donde se guarda ese dato.

Los punteros son importantes porque permiten que un programa acceda a datos de forma indirecta. El valor de un puntero decide qué dato se usará en un cálculo. En muchos lenguajes de programación, el "tipo" de un puntero (por ejemplo, si apunta a un número entero o a un texto) ayuda a la computadora a saber qué tipo de dato esperar en esa dirección.

Usos de los Punteros en Programación

Punteros en estructuras de datos

Cuando se organizan estructuras de datos como listas, colas o árboles, los punteros son esenciales. Ayudan a controlar cómo se conectan y manejan los diferentes elementos. Por ejemplo, en una lista, un puntero puede indicar cuál es el primer elemento o cuál es el siguiente.

Las direcciones a las que apuntan los punteros pueden ser "absolutas" (la dirección real en la memoria) o "relativas" (un desplazamiento desde un punto de inicio). Las direcciones relativas a veces usan menos espacio, pero requieren un cálculo extra para encontrar la dirección final.

Punteros en tablas de control

Las tablas de control usan mucho los punteros para dirigir el flujo de un programa. Un puntero dentro de una tabla puede indicar a qué subrutina (una pequeña parte de código) debe ir el programa, dependiendo de ciertas condiciones. También pueden usarse para saltar a otras partes de la tabla o volver a entradas anteriores.

Punteros y la arquitectura de la computadora

Los punteros son una forma sencilla de usar las capacidades de direccionamiento de la memoria en la mayoría de las computadoras modernas. Cada parte de la memoria tiene una dirección numérica. Si un programa tiene esa dirección, puede pedirle al sistema que le dé el valor guardado en esa ubicación.

A veces, un puntero puede intentar acceder a una dirección que no existe o que no está disponible. Esto puede causar un error en el programa. Para evitarlo, algunos sistemas tienen reglas para que los punteros solo apunten a direcciones válidas.

Punteros en lenguajes de programación

Los punteros se usan directamente en lenguajes como C, C++ y Pascal. Son fundamentales para crear referencias y para pasar datos entre diferentes partes de un programa.

En lenguajes de programación funcionales, los punteros se manejan de forma más abstracta.

Cuando se trabaja con matrices, los punteros ayudan a encontrar rápidamente un elemento específico. Si los elementos de la matriz tienen tamaños que son múltiplos de dos, el cálculo de la dirección es más eficiente. En otras estructuras de datos, como las listas enlazadas, los punteros conectan explícitamente un elemento con el siguiente.

Los punteros también se usan para "pasar parámetros por referencia". Esto significa que una función puede modificar el valor de una variable que le fue pasada, y ese cambio será visible fuera de la función.

Además, los punteros son clave para asignar y liberar memoria de forma dinámica. Cuando un programa necesita espacio en la memoria para guardar datos temporalmente, puede pedirlo y usar un puntero para acceder a él. Cuando ya no necesita ese espacio, lo libera para que otros programas puedan usarlo. Si no se libera la memoria, el programa podría usar cada vez más espacio, lo que se conoce como "fuga de memoria".

Punteros en C

En el lenguaje C, la forma básica de declarar un puntero es:

int * ptr;

Esto significa que `ptr` es un puntero que puede apuntar a un número entero (`int`).

Es importante que un puntero apunte a una dirección válida. A veces, se inicializa un puntero con un valor especial llamado "puntero nulo" (`NULL`), que significa que no apunta a nada. Intentar usar un puntero nulo puede causar que el programa se detenga.

Una vez declarado, un puntero puede apuntar a algo:

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

Aquí, `&a` significa "la dirección de la variable `a`". Así, `ptr` ahora guarda la dirección de `a`. Si `a` está en la dirección de memoria 0x8130, entonces `ptr` valdrá 0x8130.

Para acceder al valor al que apunta el puntero, se usa de nuevo el asterisco:

*ptr = 8;

Esto significa: ve a la dirección que guarda `ptr` (0x8130) y cambia el valor que está allí a 8. Ahora, si miras el valor de `a`, verás que es 8.

Matrices en C

En C, las matrices y los punteros están muy relacionados. Acceder a un elemento de una matriz como `array[i]` es lo mismo que `*(array + i)`. Esto significa que las matrices pueden verse como punteros al inicio de un bloque de memoria donde los elementos están uno al lado del otro.

Por ejemplo:

int array[5];      /* Declara 5 enteros seguidos */
int *ptr = array;  /* Las matrices pueden usarse como punteros */
ptr[0] = 1;        /* Los punteros pueden usarse con la sintaxis de matrices */
*(array + 1) = 2;  /* Las matrices pueden desreferenciarse con sintaxis de puntero */

Aquí, `array` es como un puntero al primer elemento. `array + 1` significa la dirección del segundo elemento (porque se suma el tamaño de un entero).

Es importante saber que `sizeof(array)` te dará el tamaño total de la matriz, mientras que `sizeof(ptr)` te dará el tamaño del puntero en sí.

Listas enlazadas en C

Las listas enlazadas son estructuras de datos donde cada elemento contiene un dato y un puntero al siguiente elemento.

/* la lista enlazada vacía se representa por NULL */
#define EMPTY_LIST  NULL

struct link {
    void        *datos;  /* datos de este vínculo */
    struct link *proximo;  /* siguiente enlace; EMPTY_LIST si no hay ninguno */
};

Esta definición usa punteros para conectar los elementos, formando una cadena.

Pasar por dirección usando punteros

Los punteros permiten que una función cambie el valor de una variable que le fue pasada.

#include <stdio.h>

void cambiarValor(int n) {
    n = 12; // Cambia solo la copia local
}

void cambiarPorPuntero(int *m) {
    *m = 14; // Cambia el valor original a través del puntero
}

int main(void) {
    int x = 3;

    cambiarValor(x); // x sigue siendo 3
    // x es 3

    cambiarPorPuntero(&x); // x ahora es 14
    // x es 14

    return 0;
}

En `cambiarValor`, se pasa una copia de `x`, por lo que el cambio no afecta a la `x` original. En `cambiarPorPuntero`, se pasa la dirección de `x` (`&x`), y al usar `*m = 14`, se modifica directamente el valor de `x`.

Asignación dinámica de memoria

Los punteros se usan para manejar bloques de memoria que se piden y se liberan mientras el programa se ejecuta. Esto se llama "asignación dinámica de memoria". La función `malloc()` pide un bloque de memoria y devuelve un puntero a ese bloque. La función `free()` libera la memoria que ya no se necesita.

/* Estructura para un objeto de inventario */
struct Item {
    int         id;     /* Número de parte */
    char *      name;   /* Nombre de parte */
    float       cost;   /* Costo       */
};

/* Crea un nuevo objeto Item */
struct Item * crear_item(const char *name) {
    struct Item * item;
    item = (struct Item *)malloc(sizeof(struct Item)); // Pide memoria
    // ... inicializa el item ...
    return item;
}

/* Libera un objeto Item */
void destruir_item(struct Item *item) {
    if (item == NULL) return;
    free(item->name); // Libera la memoria del nombre
    free(item);       // Libera la memoria del item
}

Punteros y hardware

En algunos sistemas, los punteros pueden usarse para controlar directamente la memoria o los dispositivos de hardware. Esto es común en la programación de microcontroladores.

int *direccion_hardware = (int *)0x7FFF; // Puntero a una dirección específica

Esto permite a los programadores interactuar directamente con componentes de la computadora.

Punteros con tipo y conversiones

En muchos lenguajes, los punteros tienen un "tipo" específico. Por ejemplo, un puntero puede ser declarado para apuntar solo a números enteros. Esto ayuda a evitar errores, ya que el lenguaje intentará que no apuntes a datos de otro tipo, como números con decimales.

En C:

int *dinero; // Puntero a un entero
char *bolsas; // Puntero a un carácter

Si intentas asignar `bolsas = dinero;`, el compilador te dará una advertencia porque son de tipos diferentes. Para forzar la asignación, se usa una "conversión de tipo" (casting):

bolsas = (char *)dinero; // Convierte el puntero de entero a carácter

Esto le dice al compilador que sabes lo que estás haciendo.

Cuando se hace aritmética con punteros, el lenguaje tiene en cuenta el tamaño del tipo al que apuntan. Si sumas 1 a un puntero a enteros de 4 bytes, el puntero se moverá 4 bytes, apuntando al siguiente entero. Si lo conviertes a un puntero a caracteres (1 byte), sumar 1 lo moverá solo 1 byte.

Haciendo los punteros más seguros

Los punteros pueden causar errores si no se usan correctamente, ya que permiten acceder directamente a la memoria. Por eso, muchos lenguajes han creado formas más seguras de usar referencias. Los punteros que acceden directamente a la memoria se llaman "punteros crudos" o "sin procesar".

Uno de los problemas es que un puntero puede apuntar a una dirección no usada o a datos que se usan para otra cosa. Lenguajes como Java usan "referencias" en lugar de punteros directos. Estas referencias solo pueden apuntar a objetos y no se pueden manipular como números, lo que previene muchos errores.

Un puntero que no tiene ninguna dirección asignada se llama "puntero salvaje". Usar un puntero salvaje puede causar un comportamiento inesperado o que el programa se detenga.

Otro problema es el "puntero colgante". Esto ocurre cuando un puntero sigue apuntando a una dirección de memoria que ya ha sido liberada. Esa memoria podría ser usada por otra parte del programa, y si el puntero colgante intenta usarla, podría causar problemas. Lenguajes con "recolector de basura" evitan esto, ya que la memoria se libera automáticamente cuando ya no hay referencias a ella.

Algunos lenguajes, como C++, tienen "punteros inteligentes". Estos punteros ayudan a gestionar la memoria automáticamente, reduciendo la posibilidad de punteros colgantes y fugas de memoria.

Puntero nulo

Un puntero nulo es un valor especial que indica que el puntero no apunta a ningún objeto válido. Se usa para representar el final de una lista o cuando una operación no pudo encontrar lo que buscaba.

En C, el valor `NULL` se usa para los punteros nulos. Si intentas usar un puntero nulo, el programa generalmente se detiene.

C. A. R. Hoare inventó la referencia nula en 1965 y la llamó "el error de mil millones de dólares" porque ha causado innumerables errores y problemas en los programas.

Indirección múltiple

En algunos lenguajes, un puntero puede apuntar a otro puntero. Esto significa que necesitas "desreferenciar" varias veces para llegar al valor final. Aunque puede hacer el programa un poco más lento, a veces es necesario para estructuras de datos complejas.

Por ejemplo, en C, para una lista enlazada, a veces se usa un puntero a un puntero para manejar el inicio de la lista de forma más eficiente.

Puntero a función

En algunos lenguajes, un puntero puede apuntar a una función o procedimiento. Esto significa que puedes guardar la dirección de una función y luego "llamar" a esa función usando el puntero. Esto permite que los programas sean más flexibles y puedan decidir qué función ejecutar en diferentes momentos.

    int  sumar(int n1, int n2);   // Una función
    int  (*puntero_a_funcion)(int, int); // Un puntero a una función

    puntero_a_funcion = &sumar; // El puntero ahora apunta a la función sumar
    int resultado = (*puntero_a_funcion)(5, 3); // Llama a la función sumar

Puntero salvaje

Un puntero salvaje es un puntero que no ha sido inicializado, es decir, no se le ha asignado ninguna dirección. Si intentas usar un puntero salvaje, puede apuntar a cualquier lugar de la memoria, lo que puede causar que el programa se comporte de forma inesperada o se detenga.

int ejemplo_puntero_salvaje(void)
{
    char *p1 = malloc(sizeof(char)); // Puntero inicializado
    char *p2;       // Puntero salvaje (no inicializado)
    *p1 = 'a';      // Esto está bien
    *p2 = 'b';      // ¡Esto causa un comportamiento impredecible!
}

En este ejemplo, `p2` no tiene una dirección válida, y al intentar escribir en `*p2`, el programa podría dañar la memoria o fallar.

Simulación con índices de matriz

Si un lenguaje no tiene punteros explícitos, a veces se puede simular su comportamiento usando índices de matriz. Una matriz grande puede actuar como un espacio de memoria, y los índices se usan como "punteros" relativos al inicio de la matriz. Esto permite acceder y manipular datos de forma similar a como lo harían los punteros.

Punteros en lenguajes de programación

Cuando un programa se ejecuta, las variables, estructuras y funciones tienen una dirección en la memoria. Los punteros son útiles para trabajar con estas direcciones, especialmente en lenguajes que permiten un control directo de la memoria.

Estructuras

Las estructuras de datos reservan espacio en la memoria para cada uno de sus miembros. Cada miembro tiene su propia dirección. Por ejemplo, en una estructura con miembros A, B y C, el puntero de la estructura completa apunta al primer miembro. La dirección de los otros miembros se calcula sumando el tamaño de los miembros anteriores.

Uniones

A diferencia de las estructuras, las uniones comparten la misma memoria para todos sus miembros. Solo usan la cantidad de memoria del miembro más grande. Por ejemplo, si una unión tiene un miembro de 1 byte y otro de 4 bytes, la unión completa usará 4 bytes, y ambos miembros compartirán ese espacio. Si cambias el valor de un miembro, podría afectar a los otros, ya que están usando el mismo espacio de memoria.

Matrices

Las matrices son similares a las estructuras, pero todos sus miembros son del mismo tipo de dato. Cada miembro tiene una dirección de memoria consecutiva. El puntero de la matriz completa es la dirección de su primer elemento.

Soporte de punteros en lenguajes

Muchos lenguajes de programación tienen diferentes formas de manejar los punteros:

Ada

Ada es un lenguaje con tipos muy estrictos. Todos los punteros (llamados "tipos de acceso") tienen un tipo y se inicializan a `null`. Si intentas usar un puntero `null`, el programa genera un error. Ada-95 permite algunas operaciones aritméticas seguras con punteros.

BASIC

Algunas versiones antiguas de BASIC tenían funciones para obtener la dirección de variables o cadenas. Las versiones más recientes, como FreeBASIC, tienen un soporte más completo para punteros, similar a C, pero con algunas diferencias.

C y C++

En C y C++, los punteros son variables que guardan direcciones de memoria y pueden ser `null`. Cada puntero tiene un tipo, pero se pueden convertir entre tipos de puntero (con cuidado). El "puntero `void`" (`void*`) puede apuntar a cualquier tipo de dato (excepto funciones), pero no se puede usar directamente sin convertirlo a un tipo específico.

La "aritmética de punteros" permite sumar o restar números a un puntero. Esto mueve el puntero por múltiplos del tamaño del tipo al que apunta. Por ejemplo, sumar 1 a un puntero a enteros lo mueve al siguiente entero en una matriz. Esto es muy potente, pero también puede causar errores si no se usa correctamente.

C#

En C#, los punteros solo se permiten en bloques de código marcados como `unsafe` (inseguro), lo que significa que requieren permisos especiales. La sintaxis es similar a C++. Los punteros a memoria gestionada (objetos controlados por el sistema) deben declararse con la palabra clave `fixed` para evitar que el "recolector de basura" los mueva mientras se usan.

COBOL

COBOL soporta punteros a variables. Los datos declarados en la sección `LINKAGE` de un programa se basan en punteros. También tiene variables de puntero declaradas con `USAGE IS POINTER` y `PROCEDURE-POINTER` para direcciones de código.

PL/I

PL/I ofrece soporte completo para punteros a todos los tipos de datos, incluyendo estructuras. Fue un lenguaje muy avanzado para su época.

D

El lenguaje D, que es un derivado de C y C++, también soporta completamente los punteros y las conversiones de tipo de C.

Eiffel

El lenguaje Eiffel usa "referencias" en lugar de punteros directos. Son de tipo seguro y no permiten aritmética de punteros, lo que ayuda a prevenir errores.

Fortran

Fortran-90 introdujo punteros de tipo estricto. Los punteros en Fortran guardan más que solo una dirección; también incluyen información sobre los límites de las matrices. Fortran-2003 añadió soporte para punteros a procedimientos.

Go

Go tiene punteros con una sintaxis similar a C, pero no permite la aritmética de punteros. Tiene recolección de basura. No usa el operador `->` (flecha); puedes usar el operador `.` directamente en un puntero para acceder a sus miembros.

Java

Java no tiene punteros explícitos. En su lugar, usa "referencias" para objetos y matrices. No hay operadores para manipular punteros directamente. Si intentas usar una referencia nula, se produce un error que puede ser manejado. Java tiene un "recolector de basura" que libera automáticamente la memoria de los objetos que ya no se usan.

Modula-2 y Oberon

Modula-2 y Oberon implementan punteros de forma estricta, similar a Pascal, pero con menos formas de eludir el sistema de tipos, lo que los hace más seguros. Algunas variantes incluyen recolección de basura.

Pascal

El estándar Pascal solo permite que los punteros apunten a variables creadas dinámicamente y no permite la aritmética de punteros. Los punteros deben tener un tipo asociado, lo que mejora la seguridad. Sin embargo, algunas implementaciones de Pascal permiten más flexibilidad, incluyendo la aritmética de punteros y la conversión entre tipos de puntero.

Pauscal

El lenguaje Pauscal tiene un fuerte soporte para punteros, permitiendo apuntar a variables, estructuras, procedimientos y más. Los punteros se usan para almacenar direcciones o crear referencias, y para convertir tipos de datos sin necesidad de herramientas externas.

Perl

Perl soporta punteros, pero se usan raramente, principalmente para interactuar con bibliotecas externas. En la mayoría de los casos, Perl usa "referencias" que son de tipo seguro y no permiten la aritmética de punteros.

|

Véase también

Kids robot.svg En inglés: Pointer (computer programming) Facts for Kids

kids search engine
Puntero (informática) para Niños. Enciclopedia Kiddle.