que - punteros a cadenas
¿Cómo se almacenan los nombres de las variables en la memoria en C? (5)
¿El compilador simplemente sustituye variable_name por 0xaaaaaaaa cada vez que lo ve?
Sí.
y si es así, ¿no tendría que usar memoria para hacer esa sustitución?
Sí. Pero es el compilador, después de compilar tu código, ¿por qué te importa la memoria?
En C, digamos que tiene una variable llamada variable_name
. Digamos que está ubicado en 0xaaaaaaaa
, y en esa dirección de memoria, tiene el entero 123. En otras palabras, variable_name
contiene 123.
Estoy buscando aclaraciones sobre el fraseo " variable_name
está ubicado en 0xaaaaaaaa
". ¿Cómo reconoce el compilador que la cadena "variable_name" está asociada con esa dirección de memoria en particular? ¿La cadena "variable_name" está almacenada en alguna parte de la memoria? ¿El compilador simplemente sustituye variable_name
por 0xaaaaaaaa
cada vez que lo ve, y si es así, no tendría que usar memoria para hacer esa sustitución?
El compilador de CA primero crea una tabla de símbolos, que almacena la relación entre el nombre de la variable y su ubicación en la memoria. Al compilar, utiliza esta tabla para reemplazar todas las instancias de la variable con una ubicación de memoria específica, como han indicado otros. Puedes encontrar mucho más en la página de Wikipedia.
Esto es lo que se llama un detalle de implementación . Si bien lo que describes es el caso en todos los compiladores que he usado, no es necesario que sea el caso. El compilador de CA podría poner todas las variables en una tabla hash y buscarlas en tiempo de ejecución (o algo así) y, de hecho, los primeros intérpretes de JavaScript hicieron exactamente eso (ahora hacen una compilación Just-In-Time que da como resultado algo mucho más crudo).
Específicamente para compiladores comunes como VC ++, GCC y LLVM: el compilador generalmente asignará una variable a una ubicación en la memoria. Las variables de alcance global o estático obtienen una dirección fija que no cambia mientras el programa se está ejecutando, mientras que las variables dentro de una función obtienen una dirección de pila , es decir, una dirección relativa al puntero de pila actual, que cambia cada vez que una función es llamado. (Esto es una simplificación excesiva). Las direcciones de pila se vuelven inválidas tan pronto como la función retorna, pero tienen el beneficio de tener efectivamente una sobrecarga para usar.
Una vez que una variable tiene una dirección asignada, ya no es necesario el nombre de la variable, por lo que se descarta. Dependiendo del tipo de nombre, el nombre puede descartarse en el tiempo de preproceso (para nombres de macro), tiempo de compilación (para variables / funciones estáticas y locales) y tiempo de enlace (para variables / funciones globales). Si se exporta un símbolo ( hecho visible para otros programas para que puedan acceder a él), el nombre generalmente permanecerá en algún lugar de una "tabla de símbolos" que ocupa una cantidad trivial de memoria y espacio en disco.
Los nombres de las variables ya no existen después de que se ejecuta el compilador (salvo casos especiales como las globales exportadas en bibliotecas compartidas o símbolos de depuración). Todo el acto de compilación está destinado a tomar esos nombres simbólicos y algoritmos representados por su código fuente y convertirlos en instrucciones nativas de la máquina. Así que sí, si tiene un nombre variable_name
global, y el compilador y el vinculador deciden ponerlo en 0xaaaaaaaa
, entonces, donde sea que se use en el código, solo se accederá a través de esa dirección.
Entonces para responder a sus preguntas literales:
¿Cómo reconoce el compilador que la cadena "variable_name" está asociada con esa dirección de memoria en particular?
La cadena de herramientas (compilador y enlazador) trabaja en conjunto para asignar una ubicación de memoria para la variable. El trabajo del compilador es realizar un seguimiento de todas las referencias, y el vinculador pone las direcciones correctas más adelante.
¿La cadena
"variable_name"
almacenada en alguna parte de la memoria?
Solo mientras el compilador se está ejecutando.
¿El compilador simplemente sustituye
variable_name
por0xaaaaaaaa
cada vez que lo ve, y si es así, no tendría que usar memoria para hacer esa sustitución?
Sí, eso es más o menos lo que sucede, excepto que es un trabajo de dos etapas con el vinculador. Y sí, usa memoria, pero es la memoria del compilador , no nada en tiempo de ejecución para su programa.
Un ejemplo puede ayudarte a entender. Probemos este programa:
int x = 12;
int main(void)
{
return x;
}
Bastante sencillo, ¿verdad? DE ACUERDO. Tomemos este programa, compilemos y observemos el desmontaje:
$ cc -Wall -Werror -Wextra -O3 example.c -o example
$ otool -tV example
example:
(__TEXT,__text) section
_main:
0000000100000f60 pushq %rbp
0000000100000f61 movq %rsp,%rbp
0000000100000f64 movl 0x00000096(%rip),%eax
0000000100000f6a popq %rbp
0000000100000f6b ret
¿Ves esa línea movl
? Está tomando la variable global (en una forma relativa a un puntero de instrucción, en este caso). No más mención de x
.
Ahora vamos a hacerlo un poco más complicado y agreguemos una variable local:
int x = 12;
int main(void)
{
volatile int y = 4;
return x + y;
}
El desmontaje para este programa es:
(__TEXT,__text) section
_main:
0000000100000f60 pushq %rbp
0000000100000f61 movq %rsp,%rbp
0000000100000f64 movl $0x00000004,0xfc(%rbp)
0000000100000f6b movl 0x0000008f(%rip),%eax
0000000100000f71 addl 0xfc(%rbp),%eax
0000000100000f74 popq %rbp
0000000100000f75 ret
Ahora hay dos instrucciones movl
y una instrucción addl
. Puedes ver que el primer movl
está inicializando y
, que se decidió estará en la pila (puntero base - 4). Luego, el siguiente movl
obtiene la x
global en un registro eax
, y el addl
agrega y
a ese valor. Pero como puede ver, las cadenas literales x
y y
ya no existen. Eran convenientes para usted , el programador, pero la computadora ciertamente no se preocupa por ellos en el momento de la ejecución.
Todas las variables son sustituidas por el compilador. Primero se sustituyen por referencias y luego el vinculador coloca direcciones en lugar de referencias.
En otras palabras. Los nombres de las variables ya no están disponibles tan pronto como el compilador se haya ejecutado