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.
- ¿Hay algún método para generar números factoriales grandes usando C ++?
- ¿Hasta qué punto puede comprimir un archivo comprimido de manera eficiente?
- ¿Por qué este programa da '0' como salida?
- Me gustan las matemáticas y la programación. ¿Qué área de cálculo funciona con ambos?
- ¿Cuál es la diferencia entre datos continuos y discretos?
(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.