¿Cuáles son algunas aplicaciones del mundo real de punteros en la programación con ejemplos?

En el nivel más bajo, todas las estructuras de datos se basan en fragmentos de memoria. Cuando agrega a su lenguaje de programación la conveniencia mínima para lidiar con esos fragmentos de memoria, termina inventando punteros. Utiliza punteros para construir todo lo demás, incluidas estructuras de datos de nivel superior (más abstractas, con más comodidades ya incorporadas).

La memoria es realmente una enorme y larga serie de bits (1-o-0), originalmente codificada como agujeros en papel, ahora cargas magnéticas en el material. Hay otra estructura allí, agregada en niveles sucesivamente más altos de abstracción; controladores de dispositivo, sistema operativo, etc., pero realmente todo se suma a una larga serie de valores de encendido y apagado, tradicionalmente representados como 1 o 0.

Debe realizar un seguimiento de dónde están esos trozos de memoria en la memoria del sistema. Lo hace manteniendo una dirección de memoria, un desplazamiento numérico en esa larga serie de bits. A medida que avanza desde el nivel de hardware, los lenguajes de programación proporcionan un mayor nivel de abstracción y más comodidades. Una de esas comodidades es un puntero.

(Nota: le animo a leer sobre esto. Es útil para un programador tener una idea general de cómo funcionan la memoria y los discos a nivel físico, dispositivo y sistema operativo, pero lo dejaré para más adelante).

Un puntero es solo una porción de memoria, un conjunto de bits, que contiene una pequeña cantidad de datos. Los datos dentro de un puntero se interpretan como un número, un desplazamiento en la larga serie de bits, el punto de partida donde escribió algunos datos. También se llama una dirección de memoria.

Como es solo un número, las personas pueden jugar con la aritmética de punteros y meterse en problemas.

También puede meterse en problemas al perder la noción de cuánto dura su fragmento y continuar leyendo más allá del final de lo que asignó, o incluso escribir más allá y sobrescribir algunos otros datos.

Dependiendo de qué tan bajo sea el idioma en el que está trabajando, puede proporcionar algunas comodidades para ayudarlo a evitar ese error. Dependiendo de cuán complejo sea su código, esas conveniencias pueden no ser de mucha ayuda :-).

Nuevamente, aumente / reduzca un nivel y su idioma puede proporcionar matrices. Asumiré que sabe qué matrices son funcionales, pero en cuanto a la implementación, una matriz es solo un concepto envuelto alrededor de un puntero a un bloque contiguo de memoria más algo de contabilidad para realizar un seguimiento del tamaño del fragmento que definió para él (es decir, el tipo de datos).

En los lenguajes de nivel medio-bajo (como C), depende de usted (o más bien de su programa) realizar un seguimiento de qué tan grande es la matriz y de qué índice está en la matriz, por lo que nuevamente puede meterse en problemas y lee o escribe involuntariamente más allá del final de la matriz.

Pero incluso los índices de matriz, cuando se llega al fondo, son conceptualmente solo otro tipo de puntero. Es solo que en lugar de trabajar en compensaciones de bits, está trabajando en compensaciones de tamaños de fragmentos.

Las estructuras de datos son un tema importante, pero solo para dar un ejemplo y ver cómo se usan los punteros, en términos concretos:

Puede implementar una lista teniendo un montón de fragmentos de memoria individuales. Tienes un puntero a la primera parte. Cada fragmento tiene espacio para dos subunidades de datos. En aras de un ejemplo fácil, digamos que cada fragmento es de 64 bits. Los primeros 32 bits son un valor flotante de 32 bits, que son los datos que desea mantener en la lista. Los segundos 32 bits son un valor de dirección de puntero de 32 bits. Ese puntero es la dirección donde comienza el segundo fragmento.

Esto se llama una lista vinculada. (Hay otros matices y florituras, etc., no salga e implemente una lista vinculada únicamente sobre la base de la descripción anterior, pero esto es lo suficientemente bueno para nuestro ejemplo).

Funcionalmente, una lista vinculada es muy parecida a una matriz, pero su acceso es más lento porque tiene que hacer todo el mantenimiento.

Si tiene una matriz de flotantes de 32 bits, por ejemplo, y desea leer el cuarto elemento, el lenguaje de programación convierte myArray [4] en:
(valor del puntero para el comienzo de la matriz) + (4 * tamaño del fragmento de la matriz)
y lee el valor en esa dirección de memoria.

Para leer el cuarto elemento en una lista vinculada, debe comenzar en el primer elemento, leer los bits de:
(dirección del primer elemento + 32 bits) a (dirección del primer elemento + 64 bits),
interpretar esos 32 bits como un valor numérico,
usa ese valor como la dirección para el siguiente elemento,
luego repita hasta llegar al elemento 4.

Por otro lado, si desea insertar un nuevo elemento en el medio, digamos como el cuarto elemento:

Con una matriz, tiene que hacer un montón de copias de datos, y tal vez incluso quedarse sin espacio, tiene que asignar espacio para una nueva matriz más grande y copiar todo a eso.

Con una lista vinculada, simplemente crea un nuevo fragmento, establece el puntero del fragmento 3 en la dirección de newchunk, establece el puntero del fragmento en la dirección del fragmento anterior y listo.

El puntero es la dirección de una variable. Es una característica muy poderosa en C y C ++. ¿Alguna vez has tratado de usar el objetivo C? No puedes aprender el objetivo C sin aprender punteros en C.

Imagine que un puntero es la dirección de un edificio en una calle como Manhattan (donde cada casa es idéntica). Cuando reenvíe su puntero (increméntelo), aterrizará en el próximo edificio idéntico. Del mismo modo, cuando retira el puntero.
El tamaño del edificio (longitud del edificio) está determinado por el tipo de puntero. Por supuesto, C tiene facilidad para cambiar el tamaño del edificio.

Creo que esta metáfora ayudará.

Hay muchos usos prácticos. En C, no puede escribir la función de intercambio sin punteros. Además, los programas de lista dinámica vinculada no se pueden escribir sin punteros.

Si planea escribir aplicaciones para iOS y Mac OS X, creo que no podrá hacer nada sin punteros porque en el objetivo C, los punteros están en casi todas partes.

Y este puntero le permite visualizar un objeto con una perspectiva diferente (por ejemplo, puede visualizar una estructura como una matriz de caracteres o enteros usando punteros).

Los punteros son una característica increíblemente poderosa una vez que comprenda su propósito y aprenda a usarlos.

Una cosa para la que me gusta usarlos es operar en un objeto dentro del método donde el puntero a un objeto se pasa como argumento.
Realmente, a menudo podría devolver el objeto modificado y volver a cargarlo, pero.
a) que hace que use dos instancias de objeto en la memoria (que, en algunas aplicaciones, puede ser bastante malo.
b) eso también lo obliga a usar solo un objeto como devolución, rechazando su posible camino para devolver un código de estado o manipulando múltiples objetos en un método.

Piense en los punteros como análogos a guardar la dirección del edificio en lugar de hacer una copia del edificio.

Por ejemplo, digamos que desea encontrar una persona específica dentro de un edificio. En lugar de duplicar todo el edificio (incluido todo su contenido) y luego verificar si la persona está dentro de él. Simplemente obtenga su dirección y verifique el edificio original si la persona está allí.

Así es como se organizan las estructuras de datos más complejas en C / C ++ / casi todos los lenguajes (tenga en cuenta que algunos lenguajes como Java / C # / Python / etc. hacen esto detrás de escena sin que el programador necesite saberlo). Es decir, en lugar de hacer un duplicado de los datos reales, simplemente almacene su dirección en la RAM como puntero. Por ejemplo, ¿qué pasa si quieres hacer una lista vinculada? Si el campo que se vincula al siguiente nodo de la lista son los datos reales, entonces acaba de crear un nodo único en crecimiento recursivo (ya que todos los datos deberán duplicarse en este nodo único). Pero si el campo que se une al siguiente nodo es solo el puntero (dirección) del siguiente, es un tamaño fijo y no es necesaria la duplicación.

Además, ¿qué pasa con los parámetros de una función? Tenga en cuenta que, de manera predeterminada, la mayoría (si no todos) se pasan por valor, es decir, una nueva copia de los datos y no el original. Por lo tanto, si lo cambia, solo está cambiando esta copia local, por lo tanto, cuando la función se complete, sus cambios se perderán (ya que esta copia local se libera de la RAM). Pero si el parámetro es un puntero (dirección) al original, puede actuar directamente sobre el original y sus cambios persistirán incluso después de que la función ya no exista.

En pocas palabras, si no utiliza punteros, sus programas no pueden ser muy complejos sin RAM infinita. Sus estructuras de datos son extremadamente difíciles. Y lo peor de todo, estaría atascado solo con el uso de variables globales para cualquier cosa que necesite permanecer modificada por más de unos pocos microsegundos.

Los punteros son ampliamente utilizados:

1. pasar datos del modo no privilegiado al modo privilegiado – contexto del SO
2. Pasar grandes estructuras de datos sin copiarlas una y otra vez: contexto de red.
3. Acceso indirecto y mapeo de registros de hardware: contexto de firmware.
4. Funciones de devolución de llamada utilizando punteros de función: contexto GUI.
5. Los punteros se utilizan para escribir funciones genéricas que funcionan en todos los tipos de datos (punteros nulos), presentes en todas partes

Listas vinculadas, sistemas de inventario para juegos, menús, en cualquier momento que desee mejorar el rendimiento en lugar de pasar por valor.

El patrón Singleton es un excelente ejemplo. Es una clase con una instancia de sí misma almacenada como un puntero. El constructor es privado, de modo que solo un método GetInstance () (asígnele el nombre que desee) puede acceder a él. Ejemplo:

clase Singleton {

público:

Singleton estático * GetInstance () {

if (instancia == NULL)

return new Singleton ();

otra instancia de retorno;

}

privado:

instancia Singleton * estática;

};

Singleton * Singleton :: instancia = NULL;

No probado, pero entiendes la idea.

Ampliando la respuesta de Jeff Kesselman, en cualquier lugar donde necesite más de 1 elemento de alguna estructura.
Si es un gráfico / árbol, tiene punteros directos como miembros de la estructura.
Si es una matriz, aún usa punteros para ir más allá del primer elemento de la matriz.

Además, si necesita crear un objeto dinámicamente en el montón (por lo general, la pila tiene un tamaño bastante limitado), también usaría punteros explícitamente (vea operadores nuevos / eliminar).

Cualquier tipo de estructura de datos basada en gráficos: árboles, listas vinculadas, tablas hash.

También cualquier código que tenga que hablar con el hardware, como los controladores de dispositivos.

Cualquier tipo de gráfico o manipulación de árbol.

Realizar un seguimiento de dónde está trabajando (p. Ej., La ubicación del cursor en un procesador de texto es generalmente un puntero a una ubicación particular en una estructura de datos o un archivo).

Lectura y escritura de ubicaciones para pilas, montones, archivos, buffers, colas, …

Punteros a dispositivos de E / S mapeados en memoria (por ejemplo, la ubicación de la memoria de video).

Se pueden usar en clases base virtuales, constructor de copias y muchas otras cosas solo son posibles con punteros.

También puede consultar la sección Poder de los punteros en Let Us C ++ por Yashwant Kanetkar para obtener más información y puede consultar algunos artículos en línea para eso también

Apunta uno de los fundamentos de C ++, por lo que si alguna vez te aventuras allí (no estoy seguro de si estás preguntando en el contexto de C o C ++), usarás mucho los punteros. Por ejemplo, polimorfismo , donde almacena un puntero a una clase base abstracta pero en realidad apuntará a clases concretas derivadas de él.