Recientemente he estado tratando de explicar los punteros de forma visual, como tarjetas de vocabulario.
Pregunta 001: Este es el dibujo de una ubicación en la memoria de la computadora. ¿Es cierto que su dirección es
0x23452
? ¿Por qué?Respuesta: Sí, porque
0x23452
describe dónde la computadora puede encontrar esta ubicación.
Pregunta 002: ¿Es cierto que el personaje
b
está almacenado dentro de la ubicación de la memoria0x23452
? ¿Por qué?Respuesta: No, porque el personaje
a
está realmente almacenado dentro de él.
Pregunta 003: ¿Es cierto que un puntero está almacenado dentro de la ubicación de la memoria
0x23452
? ¿Por qué?Respuesta: Sí, porque la dirección de la ubicación de la memoria
0x34501
se almacena dentro de ella.
Pregunta 004: ¿Es cierto que un puntero está almacenado dentro de la ubicación de la memoria
0x23452
? ¿Por qué?Respuesta: Sí, porque la dirección de otra ubicación de memoria se almacena dentro de ella.
Ahora por la parte que me tiene preocupado. Un ingeniero de software me explicó indicadores como este:
Un puntero es una variable cuyo valor es la dirección de memoria de otra variable.
Basado en las cuatro tarjetas que les he mostrado a todos, definiría los punteros de una manera ligeramente diferente:
Un puntero es una ubicación de memoria cuyo valor es la dirección de memoria de otra ubicación de memoria.
¿Es seguro decir que una variable es lo mismo que una ubicación de memoria?
Si no, ¿quién tiene razón? ¿Cuál es la diferencia entre una variable y una ubicación de memoria?
fuente
a
,0x23453
.nil
etc. cosas dentro de ellos son los valores. Puede parecer obvio para usted, pero no me sentiría cómodo dando respuestas decisivas a esas preguntas sin ver cómo se definen esos campos. Realmente no hay forma de saber sia
en la segunda imagen hay un carácter, una cadena (si son diferentes) o el nombre de una variable. Si es una cadena, entoncesnil
también es una cadena? ¿O un valor "nulo"?Respuestas:
Una variable es una construcción lógica que va a la intención de un algoritmo, mientras que una ubicación de memoria es una construcción física que describe el funcionamiento de una computadora. En términos generales, para ejecutar un programa hay un mapeo (generado por el compilador) entre la noción lógica de una variable y el almacenamiento de la computadora.
(Incluso en lenguaje ensamblador tenemos una noción de variables (lógicas) que van a algoritmo e intención, y ubicaciones de memoria (física), aunque están más combinadas en ensamblador).
Una variable es un concepto de alto (er) nivel. Una variable representa una asignación desconocida (como en matemática o programación) o un marcador de posición que puede sustituirse con un valor (como en programación: parámetros).
Una ubicación de memoria es un concepto de nivel bajo (er). Se puede usar una ubicación de memoria para almacenar un valor, a veces, para almacenar el valor de una variable. Sin embargo, un registro de CPU es otra forma de almacenar el valor de algunas variables. Los registros de la CPU también son ubicaciones de almacenamiento de nivel bajo (er), pero no son ubicaciones de memoria ya que no tienen direcciones, solo nombres.
En cierto sentido, una variable es un mecanismo de abstracción para expresar la intención del programa, mientras que una ubicación de memoria es una entidad física del entorno de procesamiento que proporciona almacenamiento y recuperación.
No podemos decir con certeza. El hecho de que haya un valor allí que funcione como una dirección, no significa que sea esa dirección, sino que podría ser el entero (decimal) 144466. No podemos hacer suposiciones sobre la interpretación de valores simplemente basados en cómo aparecen numéricamente.
Esta es de hecho una pregunta extraña. Esperan algunos supuestos basados en los cuadros, sin embargo, tengamos en cuenta que las direcciones aumentan en 1 para cada cuadro. En cualquier computadora moderna, eso significaría que cada caja puede contener un byte a direccionamiento byte ha sido la norma durante décadas. Sin embargo, un byte tiene solo 8 bits y puede variar de 0 a 255 (para valores sin signo); Sin embargo, muestran un valor mucho mayor almacenado en una de estas direcciones, por lo que es muy sospechoso. (Esto podría funcionar si se tratara de una máquina dirigida por palabras, pero no dice eso, y pocas máquinas lo son hoy, aunque algunas máquinas educativas lo son).
Si bien hay situaciones en las que este pensamiento es correcto, aquí estás mezclando metáforas. La noción de una variable va al algoritmo y su intención: no es necesario suponer que todas las variables tienen ubicaciones de memoria. Algunas variables (especialmente las matrices) tienen ubicaciones de memoria porque las ubicaciones de memoria admiten el direccionamiento (mientras que los registros de la CPU solo se pueden nombrar sin indexar).
Para la ejecución, hay un mapeo lógico entre variables y declaraciones y ubicaciones de memoria del procesador y secuencias de instrucciones del procesador. Una variable cuyo valor nunca cambia (por ejemplo, una constante) ni siquiera requiere necesariamente una ubicación de memoria, ya que el valor puede reproducirse a voluntad (por ejemplo, según sea necesario para las secuencias de código generadas por el compilador).
fuente
for
índice de bucle cuando el compilador decide desenrollar completamente el bucle. En ninguna parte del código de salida producido (ya sea ensamblado o código de máquina o código de byte) hay una ubicación de memoria en la que se almacena el contador de bucle. Pero sigue siendo una variable.source
en una matriz de igual longitud,dest
un bucle codificadofor (int i=0; i<8; ++i) dest[i] = source[i];
podría compilarse en algo equivalente a la repetición deldest++ = source++;
número correspondiente de veces. Con el contador de bucle en sí mismo no hay evidencia (ni siquiera en el registro), y solo el número de repeticiones le informa sobre la condición del bucle.No. La variable y la ubicación de la memoria son dos abstracciones en dos niveles de abstracción diferentes. Las variables y los punteros son conceptos de nivel superior a nivel de código / lenguaje, la ubicación de memoria es un concepto de nivel inferior a nivel de máquina. Una vez que un código ha sido compilado en un ejecutable, ya no hay ninguna variable. Intentar hablar sobre la ubicación de la memoria y las variables de esta manera es un error categórico.
Se puede implementar una variable usando la memoria, pero no siempre como un compilador puede optimizar un cálculo y hacer todos los cálculos relacionados con una variable completamente en registros, o puede poner una sola variable en múltiples ubicaciones de memoria, o puede usar una sola memoria ubicación para múltiples variables.
Esta serie de tarjetas flash es tan confusa que no solo no están bien, sino que ni siquiera están equivocadas.
fuente
Once a code had been compiled into an executable, there's no longer any variables.
Eso es algo con lo que posiblemente no estoy de acuerdo. Es correcto que su variable tal como la conoce (es decir, por ese nombre) ya no existe, pero su frase parece sugerir que el ejecutable compilado solo usa direcciones de memoria. Eso no es correcto Su ejecutable compilado pero no ejecutándose no tiene idea de qué direcciones de memoria usará cuando se ejecute. El concepto de una variable (es decir, una referencia reutilizable a cualquier dirección de memoria que se asignará en tiempo de ejecución) todavía existe dentro del ejecutable compilado.Las variables son construcciones del lenguaje . Tienen un nombre, residen dentro de un ámbito, pueden ser referenciados por otras partes del código, etc. Son una entidad lógica . El compilador es libre de implementar esta construcción del lenguaje de la forma que desee, siempre que el comportamiento observable sea el prescrito por el estándar del lenguaje. Como tal, la variable ni siquiera necesita almacenarse en ningún lugar si el compilador puede demostrar que no es necesario.
Las ubicaciones de memoria son un concepto de hardware . Significan un lugar en la memoria virtual / física. Cada ubicación de memoria tiene exactamente una dirección física y cualquier cantidad de direcciones virtuales que puedan usarse para manipularla. Pero siempre hay exactamente un byte almacenado en cada ubicación de memoria.
Los punteros son un tipo especial de valores . Decir que algo es un puntero es similar a decir que algo es de tipo
double
. Significa cuántos bits se usan para el valor y cómo se interpretan esos bits, pero no significa que este valor esté almacenado en una variable, ni tampoco significa que este valor esté almacenado en la memoria.Para dar un ejemplo en C: cuando tengo una matriz 2D
int foo[6][7];
y accedo a un elemento de la mismafoo[1][2]
, entoncesfoo
hay una variable que contiene una matriz. Cuandofoo
se usa en este contexto, se convierte en un puntero al primer elemento de la matriz. Este puntero no se almacena en ninguna variable, ni se almacena en la memoria, su valor solo se genera dentro de un registro de la CPU, se usa y luego se olvida. Del mismo modo, la expresiónfoo[1]
se convierte en otro puntero en este contexto, que, de nuevo, no está en una variable, no se almacena en la memoria, sino que se calcula en la CPU, se usa y se olvida. Los tres conceptos variables , ubicación de memoria y puntero son realmente tres conceptos diferentes.Por cierto, realmente quise decir "siempre hay exactamente un byte almacenado en cada ubicación de memoria". Este no era el caso en la edad de piedra de la informática hace unos cincuenta años, pero es cierto para prácticamente todo el hardware que se usa actualmente. Cada vez que almacena un valor en la memoria que es mayor que un byte, en realidad está utilizando varias ubicaciones de memoria consecutivas. Es decir (suponiendo el orden de bytes de Big Endian), el número 0x01234567 se almacena en la memoria como
(Pequeñas máquinas endian como la arquitectura X86 almacenan los bytes en orden inverso). Esto también es cierto para los punteros: un puntero en una máquina de 64 bits se almacena en ocho bytes consecutivos, cada uno con su propia dirección de memoria. No puede mirar una celda de memoria y decir: "¡Oh, esto es un puntero!" Siempre ves bytes cuando miras la memoria .
fuente
Déjame concentrarme en tu pregunta real: "¿Quién tiene razón?" al comparar estas dos declaraciones:
La respuesta a esto es ninguna . La primera habla de una "dirección de memoria de otra variable", pero las variables no necesariamente tienen direcciones de memoria, como ya explicaron las otras respuestas. El segundo dice "un puntero es una ubicación de memoria", pero un puntero es literalmente solo un número, que puede almacenarse en una variable, pero como antes, una variable no necesariamente tiene una dirección de memoria.
Algunos ejemplos para declaraciones más precisas:
"Un puntero es un número que representa la dirección de memoria de una ubicación de memoria", o
"Una variable de puntero es una variable cuyo valor es la dirección de memoria de una ubicación de memoria".
"Una dirección de memoria puede contener un puntero que representa la dirección de memoria de una ubicación de memoria".
Tenga en cuenta que a veces el término "puntero" se usa como un atajo para "variable de puntero", lo cual está bien siempre que no genere confusión.
fuente
Ciertamente no diría que un puntero es una ubicación de memoria que contiene una dirección. Por un lado, no conozco una arquitectura en la que
0x23453
pueda caber en un solo byte. :) Incluso si elimina manualmente la distinción byte / palabra, todavía tiene el problema de que cada ubicación de memoria contiene una dirección. Las direcciones son solo números, y el contenido de la memoria son solo números.Creo que el truco aquí es que el "puntero" describe la intención humana , no una característica particular de la arquitectura. Es similar a cómo un "carácter" o una "cadena" no es una cosa concreta que se puede ver en la memoria: todos estos son solo números, pero funcionan como cadenas porque así es como se tratan. "Puntero" simplemente significa un valor destinado a ser utilizado como una dirección.
Honestamente, si su objetivo es enseñar un lenguaje en particular (¿Objetivo C?), No estoy seguro de que sacar la cinta de memoria clásica sea tan útil. Ya está diciendo mentiras blancas al mostrar valores escritos y valores demasiado grandes para un byte. Enseñe semántica, no mecánica: la idea clave sobre los punteros es que proporcionan indirección , que es una herramienta enormemente útil para comprender.
Creo que una buena comparación podría ser con una URL, que le dice dónde encontrar algunos datos, pero no son los datos en sí. Escúchame:
Rara vez se importa lo que la URL en realidad es ; La gran mayoría de ellos están atrapados en enlaces con nombres. Mucha gente usa Internet sin saber exactamente cómo una URL da como resultado una página; algunas personas son ajenas a las URL por completo.
No todas las cadenas son URL o están destinadas a ser utilizadas como URL.
Si intenta visitar una URL falsa, o una página que solía existir pero que ha sido eliminada, aparece un error.
Una URL puede apuntar a una imagen, algo de texto, algo de música o cualquier número de otros elementos individuales, o puede apuntar a una página con una variedad de cosas contenidas dentro. Es muy común tener un montón de páginas con diseños similares pero con datos diferentes.
Si crea una página web y desea hacer referencia a datos en alguna otra página web, no necesita copiar y pegar todo; solo puedes hacer un enlace a él.
Cualquier número de otras páginas puede enlazar a la misma URL.
Si tiene una colección de páginas similares, puede hacer una página de índice que enumere los enlaces a todas ellas, o simplemente puede tener un "siguiente" enlace en la parte inferior de la página 1 que lo lleva a la página 2, y así sucesivamente. Las ventajas y desventajas de ambos enfoques son inmediatamente obvias, especialmente si considera lo que el webmaster debería hacer para agregar o eliminar páginas en varios lugares.
Esta analogía hace que sea muy claro lo que son punteros a , que es fundamental para la comprensión de ellos - de lo contrario, simplemente parecen arbitrarias, complicado, y sin sentido. Comprender cómo funciona algo es mucho más fácil si ya comprende lo que hace y por qué es útil. Si ya ha internalizado que un puntero es un cuadro negro que le dice dónde está otra cosa, y luego aprende sobre las complejidades del modelo de memoria, entonces representar punteros como direcciones es bastante obvio. Además, la enseñanza de la semántica colocará a sus estudiantes en un lugar mucho mejor para comprender e inventar otras formas de indirección, ¡lo cual es bueno cuando la mayoría de los idiomas principales no tienen punteros!
fuente
every memory location contains an address
- Cada ubicación de memoria tiene una dirección. No está contenido en ninguna parte, excepto tal vez en una variable de puntero.Sé que ya has aceptado una respuesta, y esta pregunta ya tiene cinco respuestas, pero hay un punto que no mencionan, uno que creo que te hizo tropezar. Los libros de texto de CS a menudo intentan ser agnósticos acerca de la elección del lenguaje de programación, lo que lleva a la suposición implícita de que la terminología utilizada para describir las cosas es universal. No lo es
En C, el operador de comercio unario se denomina operador de "dirección de". Los programadores de C no dudarían en decir que la expresión
&x
evalúa la dirección de la variable x. Por supuesto, significan "la dirección de memoria en la que se almacena el valor de la variable x", pero nadie es tan pedante en una conversación informal. En C, la palabra "puntero" generalmente se refiere al tipo de datos de una variable destinada a tener una dirección de memoria como su valor. O, de forma equivalente, el tipo de datos del valor. Pero algunas personas usarían "puntero" como el valor mismo.En Java, todas las variables de tipo de objeto o matriz se comportan de manera muy similar a los punteros C (excepto la aritmética de punteros), pero los programadores de Java los llaman referencias, no punteros.
C ++ considera que las referencias y los punteros son conceptos diferentes. Están relacionados, pero no son exactamente lo mismo, por lo que los programadores de C ++ tienen que hacer la distinción en la conversación. El ampersand se lee como "dirección de" en algunos contextos y "referencia a" en otros.
Así es como un programador en C podría describirlo, usando "un puntero" en el mismo sentido que "un int". (Como en "un puntero contiene una dirección de memoria mientras que un int contiene un número entero dentro de un cierto rango").
Esa es una forma extraña de decirlo, porque requiere una definición muy flexible e informal de "es".
Sería más claro decir que una dirección de memoria es la ubicación en la memoria donde se almacena el valor de una variable. (De acuerdo, no todas las variables se almacenan en la memoria, debido a las optimizaciones del compilador, pero cualquier variable cuya dirección se tome
&x
será.)fuente
La declaración Un puntero es una variable cuyo valor es la dirección de memoria de otra variable está demasiado simplificada. Pero cuando el lector comprenda qué es exactamente una ubicación de memoria y cómo difiere de una variable, ya comprenderá qué es exactamente un puntero y, por lo tanto, ya no tendrá que confiar en esta explicación inexacta.
La declaración Un puntero es una ubicación de memoria cuyo valor es la dirección de memoria de otra ubicación de memoria es incorrecta. El valor de un puntero no necesita almacenarse en una ubicación de memoria, y es discutible si un puntero necesita apuntar a una ubicación de memoria, dependiendo de la definición prevista de "memoria".
Una ubicación de memoria es uno de los múltiples lugares posibles donde se pueden almacenar datos. Esos datos pueden ser una variable o parte de una variable. Las variables son una forma de etiquetar datos.
fuente
Esta respuesta se centra en C y C ++; eso parece apropiado ya que su pregunta se refiere a punteros que son una parte más integral de C / C ++ que de otros lenguajes. La mayor parte de esta publicación se aplicará a la mayoría de los lenguajes compilados sin un tiempo de ejecución complejo (como Pascal o Ada, pero no como Java o C #).
Las buenas respuestas ya dadas enfatizan que una variable es una construcción del lenguaje en un nivel más abstracto que la memoria física. Sin embargo, me gustaría enfatizar que esta abstracción tiene cierta lógica y sistema:
La abstracción consiste principalmente en usar un nombre en lugar de una dirección literal.
La idea principal es que una variable es un identificador con nombre para un objeto escrito; Los objetos en C / C ++ generalmente están en la memoria. Luego, los idiomas agregan algunas sutilezas relacionadas con la administración de por vida y el cálculo de datos para las conversiones de tipos. El concepto de variables es más abstracto que las direcciones físicas porque en realidad no nos importa el valor numérico de las direcciones o la ubicación exacta de las funciones en la memoria. Simplemente los nombramos y luego los abordamos por nombre, y el compilador, el enlazador y el sistema de tiempo de ejecución se ocupan de los detalles arenosos.
Y no pretenda que C / C ++ son agnósticos de memoria: después de todo, existe el operador de dirección universalmente aplicable. Sí, es cierto, no puede tomar la dirección de una variable C en la clase de almacenamiento de registro; pero cuando usaste uno por última vez? Es una excepción especial al concepto general, no un rechazo total del argumento. La regla general es, por el contrario, que tomar la dirección de una variable realmente obliga al compilador a crear un objeto en la memoria, incluso si no lo hiciera de otra manera (por ejemplo, con constantes). El concepto de "identificador con nombre" también es un buen paradigma para las referencias de C ++: una referencia es solo otro nombre para el mismo objeto.
Cuando escribí el ensamblador en línea para 68k, fue agradable ver cómo podría usar nombres de variables como compensaciones para direccionar registros (¡y podría usar los nombres de variables declaradas en
register
lugar de los nombres de registro de metal desnudo!). Para el compilador, una variable es un desplazamiento de dirección constante. Para reiterar: las variables se denominan identificadores, generalmente para objetos en la memoria.fuente
Parece que la pregunta está dirigida a un lenguaje popular formado al aumentar el Estándar C con la garantía adicional "En los casos en que algunas partes del Estándar o la documentación de una implementación describen el comportamiento de alguna acción, y otra parte lo clasifica como indefinido , domina la primera parte ", así como una definición de" variable "consistente con el uso del término por otros idiomas.
En ese idioma, cada ubicación de memoria se puede ver como un buzón numerado que siempre contiene algún número (generalmente ocho) de bits, cada uno de los cuales puede ser independientemente cero o uno. Las ubicaciones de memoria generalmente se organizan en filas de dos, cuatro u ocho. y algunas operaciones se procesan en múltiples ubicaciones de memoria consecutivas a la vez. Dependiendo de la máquina, algunas operaciones que operan en grupos de dos, cuatro u ocho ubicaciones de memoria pueden estar limitadas a operar en ubicaciones dentro de una sola fila. Además, mientras que algunas máquinas pueden tener una sola habitación de buzones numerados consecutivamente, otros pueden tener múltiples grupos disjuntos de buzones numerados.
Una variable identifica un rango de ubicaciones de memoria que están asociadas exclusivamente a ella, y un tipo según el cual esas ubicaciones de memoria deben ser interpretadas. La lectura de una variable hará que los bits dentro de sus ubicaciones de almacenamiento asociadas se interpreten de manera apropiada para el tipo de variable, y escribir una variable hará que los bits asociados se establezcan de manera apropiada para su tipo y valor.
Una dirección encapsula cualquier información necesaria para identificar un buzón. Esto puede almacenarse como un número simple o como algún tipo de designador de grupo junto con el número de un buzón dentro de ese grupo.
La aplicación del
&
operador a una variable generará un puntero que encapsula la dirección y el tipo de la misma. La aplicación de unario*
u[]
operador a un puntero hará que los bits de los buzones que comienzan en la dirección encapsulada se interpreten o establezcan de manera apropiada para el tipo encapsulado.fuente
Llego tarde a esta fiesta, pero no puedo resistirme a poner mis 2 centavos.
En estos momentos, ¿cuál es la diferencia entre los valores almacenados en estas ubicaciones de memoria?
Tiempo 1
Tiempo 2
Respuesta correcta: nada. Todos son valores idénticos que se presentan con diferentes interpretaciones de su significado.
¿Cómo sé eso? Porque yo fui quien inventó esto. Realmente aún no lo sabes.
Te encuentras con algo que yo llamo el problema fuera de banda . Aquí no se almacena cómo interpretar correctamente el significado de estos valores. Ese conocimiento se almacena en otro lugar. Sin embargo, cuando presenta estos valores en papel, pone esa interpretación. Eso significa que ha agregado información que simplemente no existe en estas ubicaciones de memoria.
Por ejemplo, los valores aquí son idénticos, pero solo sabe que eso es cierto si está en lo correcto cuando asume que una codificación de caracteres ASCII / UTF-8 es cómo obtuve el primero, en lugar de decir EBCDIC . Y también debe suponer que la segunda es expresiones hexadecimales de los valores numéricos almacenados en esas ubicaciones de memoria, que podrían ser punteros a otras direcciones, en lugar de decir referencias a cadenas que comienzan con "0x". :PAGS
Nada almacenado en estas ubicaciones de memoria le indica que ninguno de esos supuestos es correcto. Esa información puede ser almacenada. Pero se almacenaría en otro lugar.
Este es el problema de presentación . No puede expresar ningún número sin antes acordar cómo presentarlo. Puede apoyarse en suposiciones, convenciones y contexto, pero si lo analiza profundamente, cuando la presentación no está explícitamente definida, la única respuesta verdaderamente correcta es "no hay suficiente información".
fuente