¿Los declaradores de tipos de datos como "int" y "char" se almacenan en la RAM cuando se ejecuta un programa en C?

74

Cuando se ejecuta un programa en C, los datos se almacenan en el montón o en la pila. Los valores se almacenan en direcciones RAM. Pero, ¿qué pasa con los indicadores de tipo (por ejemplo, into char)? ¿También están almacenados?

Considere el siguiente código:

char a = 'A';
int x = 4;

Leí que A y 4 están almacenados en direcciones RAM aquí. Pero ¿qué pasa ay x? Más confuso, ¿cómo sabe la ejecución que aes un char y xes un int? Quiero decir, ¿se menciona el inty charen algún lugar de la RAM?

Digamos que un valor se almacena en algún lugar de la RAM como 10011001; si soy el programa que ejecuta el código, ¿cómo sabré si este 10011001 es un charo un int?

Lo que no entiendo es cómo sabe la computadora, cuando lee el valor de una variable de una dirección como 10001, si es un into char. Imagina que hago clic en un programa llamado anyprog.exe. Inmediatamente el código comienza a ejecutarse. ¿Este archivo ejecutable incluye información sobre si las variables almacenadas son del tipo into char?

usuario16307
fuente
24
Esta información se pierde totalmente en tiempo de ejecución. Usted (y su compilador) deben asegurarse de antemano de que la memoria se interpretará correctamente. ¿Es esta la respuesta que buscabas?
5gon12eder
44
No lo hace. Debido a que supone que sabes lo que estás haciendo, toma lo que encuentre en la dirección de memoria que proporcionaste y lo escribe en stdout. Si lo que fue escrito corresponde a un personaje legible, eventualmente aparecerá en la consola de alguien como un personaje legible. Si no corresponde, aparecerá como un galimatías, o posiblemente un personaje legible al azar.
Robert Harvey
22
@ user16307 La respuesta breve es que, en los idiomas tipados estáticamente, cada vez que imprime un carácter, el compilador producirá un código diferente al que imprimiría un int. En el tiempo de ejecución ya no hay ningún conocimiento que xsea ​​un carácter, pero es el código de impresión de caracteres que se ejecuta, porque eso es lo que seleccionó el compilador.
Ixrec
13
@ user16307 Siempre se almacena como la representación binaria del número 65. Si se imprime como 65 o como A depende del código que produjo su compilador para imprimirlo. No hay metadatos junto a los 65 que dicen que en realidad es un char o un int (al menos, no en lenguajes estáticamente tipados como C).
Ixrec
2
El entender completamente los conceptos que preguntar acerca de aquí y ponerlas en práctica por sí mismo, es posible que desee tomar un curso compilador, por ejemplo, de uno Coursera
mucaho

Respuestas:

122

Para abordar la pregunta que ha publicado en varios comentarios (que creo que debería editar en su publicación):

Lo que no entiendo es cómo sabe la computadora cuando lee el valor de una variable y una dirección como 10001 si es un int o char. Imagine que hago clic en un programa llamado anyprog.exe. Inmediatamente el código comienza a ejecutarse. ¿Este archivo exe incluye información sobre si las variables se almacenan como en o char?

Así que vamos a ponerle un código. Digamos que escribes:

int x = 4;

Y supongamos que se almacena en la RAM:

0x00010004: 0x00000004

La primera parte es la dirección, la segunda parte es el valor. Cuando su programa (que se ejecuta como código de máquina) se ejecuta, todo lo que ve 0x00010004es el valor 0x000000004. No 'conoce' el tipo de estos datos, y no sabe cómo 'se supone' que se utilizarán.

Entonces, ¿cómo determina su programa lo correcto? Considera este código:

int x = 4;
x = x + 5;

Tenemos una lectura y una escritura aquí. Cuando su programa lee xde la memoria, encuentra 0x00000004allí. Y su programa sabe agregarlo 0x00000005. Y la razón por la cual su programa 'sabe' que esta es una operación válida, es porque el compilador asegura que la operación es válida a través de la seguridad de tipo. Su compilador ya ha verificado que puede agregar 4y 5juntos. Entonces, cuando se ejecuta su código binario (el exe), no tiene que hacer esa verificación. Simplemente ejecuta cada paso a ciegas, suponiendo que todo esté bien (las cosas malas suceden cuando en realidad están bien, no están bien).

Otra forma de pensar es así. Te doy esta información:

0x00000004: 0x12345678

Mismo formato que antes: dirección a la izquierda, valor a la derecha. ¿De qué tipo es el valor? En este punto, usted conoce tanta información sobre ese valor como su computadora cuando ejecuta código. Si le dijera que agregue 12743 a ese valor, podría hacerlo. No tienes idea de lo que las repercusiones de esa operación serán en todo el sistema, pero la adición de dos números es algo que es realmente bueno en, por lo que podría hacerlo. ¿Eso hace que el valor sea un int? No necesariamente: todo lo que ves son dos valores de 32 bits y el operador de suma.

Tal vez parte de la confusión es recuperar los datos. Si tenemos:

char A = 'a';

¿Cómo sabe la computadora mostrar aen la consola? Bueno, hay muchos pasos para eso. El primero es ir a Ala ubicación de s en la memoria y leerlo:

0x00000004: 0x00000061

El valor hexadecimal para aen ASCII es 0x61, por lo que lo anterior podría ser algo que vería en la memoria. Entonces, nuestro código de máquina conoce el valor entero. ¿Cómo sabe convertir el valor entero en un carácter para mostrarlo? En pocas palabras, el compilador se aseguró de poner todos los pasos necesarios para hacer esa transición. Pero su computadora (o el programa / exe) no tiene idea de cuál es el tipo de datos. Ese valor de 32 bits podría ser cualquier cosa - int, char, la mitad de una double, un puntero, que forma parte de una matriz, parte de una string, parte de una instrucción, etc.


Aquí hay una breve interacción que su programa (exe) podría tener con la computadora / sistema operativo.

Programa: quiero comenzar. Necesito 20 MB de memoria.

Sistema operativo: encuentra 20 MB de memoria libre que no están en uso y los entrega

(La nota importante es que esto podría volver algún 20 MB libres de memoria, ni siquiera tienen que ser contiguos. En este punto, el programa ahora puede operar dentro de la memoria que tiene sin hablar con el sistema operativo)

Programa: voy a suponer que el primer punto en la memoria es una variable entera de 32 bits x.

(El compilador se asegura de que los accesos a otras variables nunca toquen este punto en la memoria. No hay nada en el sistema que diga que el primer byte es variable x, o que la variable xes un número entero. Una analogía: tienes una bolsa. Le dices a la gente que solo pondrás bolas de color amarillo en esta bolsa. Cuando alguien más tarde saque algo de la bolsa, sería sorprendente que sacara algo azul o un cubo, algo salió terriblemente mal. Lo mismo ocurre con las computadoras: su el programa ahora supone que el primer punto de memoria es la variable x y que es un número entero. Si alguna vez se escribe algo más sobre este byte de memoria o se supone que es otra cosa, ha sucedido algo horrible. El compilador asegura que este tipo de cosas no no suceda)

Programa: ahora escribiré 2en los primeros cuatro bytes donde supongo que xestá en.

Programa: quiero agregar 5 a x.

  • Lee el valor de X en un registro temporal

  • Agrega 5 al registro temporal

  • Almacena el valor del registro temporal de nuevo en el primer byte, que todavía se supone que es x.

Programa: voy a asumir que el siguiente byte disponible es la variable char y.

Programa: escribiré aen variable y.

  • Se usa una biblioteca para encontrar el valor de byte para a

  • El byte se escribe en la dirección que el programa supone y.

Programa: quiero mostrar el contenido de y

  • Lee el valor en el segundo punto de memoria

  • Utiliza una biblioteca para convertir del byte a un carácter.

  • Utiliza bibliotecas de gráficos para alterar la pantalla de la consola (configuración de píxeles de negro a blanco, desplazamiento de una línea, etc.)

(Y continúa desde aquí)

Lo que probablemente te está colgando es: ¿qué sucede cuando el primer lugar en la memoria ya no está x? o el segundo ya no es y? ¿Qué sucede cuando alguien lee xcomo un charo ycomo un puntero? En resumen, suceden cosas malas. Algunas de estas cosas tienen un comportamiento bien definido, y algunas tienen un comportamiento indefinido. El comportamiento indefinido es exactamente eso: cualquier cosa puede suceder, de la nada a fallar el programa o el sistema operativo. Incluso el comportamiento bien definido puede ser malicioso. Si puedo cambiar xa un puntero a mi programa y hacer que su programa lo use como puntero, entonces puedo hacer que su programa comience a ejecutar mi programa, que es exactamente lo que hacen los piratas informáticos. El compilador está ahí para ayudar a asegurarnos de que no lo usemos int xcomostringy cosas de esa naturaleza. El código de la máquina en sí no conoce los tipos, y solo hará lo que las instrucciones le indiquen. También se descubre una gran cantidad de información en tiempo de ejecución: ¿qué bytes de memoria puede usar el programa? ¿ xComienza en el primer byte o el 12?

Pero puedes imaginar lo horrible que sería escribir programas como este (y puedes hacerlo en lenguaje ensamblador). Empiezas por 'declarar' tus variables: te dices a ti mismo que el byte 1 es x, el byte 2 es y, y a medida que escribes cada línea de código, cargando y almacenando registros, tú (como humano) tienes que recordar cuál es xy cuál uno es y, porque el sistema no tiene idea. Y usted (como humano) tiene que recordar qué tipos xy qué yson, porque una vez más, el sistema no tiene idea.

Shaz
fuente
Explicación asombrosa. Solo la parte que escribió "¿Cómo sabe convertir el valor entero en un carácter para mostrarlo? En pocas palabras, el compilador se aseguró de realizar todos los pasos necesarios para hacer esa transición". Todavía está nublado para mí. Digamos que la CPU obtuvo 0x00000061 del registro de RAM. Desde este punto, ¿está diciendo que hay otras instrucciones (en el archivo exe) que hacen esa transición a lo que vemos en la pantalla?
usuario16307
2
@ user16307 sí, hay instrucciones adicionales. Cada línea de código que escriba puede convertirse potencialmente en muchas instrucciones. Hay instrucciones para determinar qué personaje usar, hay instrucciones para qué píxeles modificar y a qué color cambian, etc. También hay código que realmente no se ve. Por ejemplo, usar std :: cout significa que estás usando una biblioteca. Su código para escribir en la consola puede ser solo una línea, pero las funciones que llame serán más líneas, y cada línea puede convertirse en muchas instrucciones de máquina.
Shaz
8
@ user16307 Otherwise how can console or text file outputs a character instead of int Porque hay una secuencia diferente de instrucciones para generar el contenido de una ubicación de memoria como un entero o como caracteres alfanuméricos. El compilador conoce los tipos de variables, elige la secuencia de instrucciones apropiada en el momento de la compilación y la registra en el EXE.
Charles E. Grant
2
Encontraría una frase diferente para "El código de bytes en sí", ya que el código de bytes (o código de bytes) generalmente se refiere a un lenguaje intermedio (como Java Bytecode o MSIL), que en realidad podría almacenar estos datos para que el tiempo de ejecución los aproveche. Además, no está del todo claro a qué se supone que se refiere el "código de bytes" en ese contexto. De lo contrario, buena respuesta.
jpmc26
66
@ user16307 Intenta no preocuparte por C ++ y C #. Lo que dicen estas personas está muy por encima de su comprensión actual de cómo funcionan las computadoras y los compiladores. A los efectos de lo que intenta comprender, el hardware NO sabe nada sobre los tipos, char o int o lo que sea. Cuando le dijo al compilador que alguna variable era un int, generó un código ejecutable para manejar una ubicación de memoria COMO SI fuera un int. La ubicación de la memoria en sí no contiene información sobre los tipos; es solo que su programa decidió tratarlo como un int. Olvídese de todo lo que escuchó sobre la información del tipo de tiempo de ejecución.
Andres F.
43

Creo que su pregunta principal parece ser: "Si el tipo se borra en tiempo de compilación y no se retiene en tiempo de ejecución, entonces, ¿cómo sabe la computadora si debe ejecutar un código que lo interpreta como un intcódigo que lo interpreta como un char? "

Y la respuesta es ... la computadora no. Sin embargo, el compilador sí lo sabe, y en primer lugar simplemente habrá puesto el código correcto en el binario. Si la variable se escribiera como char, entonces el compilador no pondría el código para tratarla como inten el programa, colocaría el código para tratarla como a char.

No hay razones para retener el tipo en tiempo de ejecución:

  • Escritura dinámica: en la escritura dinámica, la verificación de tipos ocurre en tiempo de ejecución, por lo que, obviamente, el tipo debe conocerse en tiempo de ejecución. Pero C no se escribe dinámicamente, por lo que los tipos se pueden borrar de forma segura. (Sin embargo, tenga en cuenta que este es un escenario muy diferente. Los tipos dinámicos y los tipos estáticos no son realmente lo mismo, y en un lenguaje de escritura mixto, aún podría borrar los tipos estáticos y solo mantener los tipos dinámicos).
  • Polimorfismo dinámico: si ejecuta un código diferente según el tipo de tiempo de ejecución, debe mantener el tipo de tiempo de ejecución. C no tiene polimorfismo dinámico (realmente no tiene ningún polimorfismo en absoluto, excepto en algunos casos especiales codificados, por ejemplo, el +operador), por lo que no necesita el tipo de tiempo de ejecución por ese motivo. Sin embargo, de nuevo, el tipo de tiempo de ejecución es algo diferente al tipo estático de todos modos, por ejemplo, en Java, en teoría, podría borrar los tipos estáticos y aún mantener el tipo de tiempo de ejecución para el polimorfismo. Tenga en cuenta también que si descentraliza y especializa el código de búsqueda de tipos y lo coloca dentro del objeto (o clase), entonces tampoco necesariamente necesita el tipo de tiempo de ejecución, por ejemplo, C ++ vtables.
  • Reflexión en tiempo de ejecución: si permite que el programa reflexione sobre sus tipos en tiempo de ejecución, entonces obviamente necesita mantener los tipos en tiempo de ejecución. Puede ver esto fácilmente con Java, que mantiene los tipos de primer orden en tiempo de ejecución, pero borra los argumentos de tipo a tipos genéricos en tiempo de compilación, por lo que solo puede reflexionar sobre el constructor de tipos ("tipo sin formato") pero no el argumento de tipo. Una vez más, C no tiene reflejo de tiempo de ejecución, por lo que no necesita mantener el tipo en tiempo de ejecución.

La única razón para mantener el tipo en tiempo de ejecución en C sería para la depuración, sin embargo, la depuración generalmente se realiza con la fuente disponible, y luego simplemente puede buscar el tipo en el archivo fuente.

Type Erasure es bastante normal. No afecta la seguridad de los tipos: los tipos se verifican en el momento de la compilación, una vez que el compilador está convencido de que el programa es seguro, los tipos ya no son necesarios (por esa razón). No afecta el polimorfismo estático (también conocido como sobrecarga): una vez que se completa la resolución de sobrecarga y el compilador ha elegido la sobrecarga correcta, ya no necesita los tipos. Los tipos también pueden guiar la optimización, pero nuevamente, una vez que el optimizador ha elegido sus optimizaciones basadas en los tipos, ya no las necesita.

La retención de tipos en tiempo de ejecución solo es necesaria cuando desea hacer algo con los tipos en tiempo de ejecución.

Haskell es uno de los lenguajes de tipo estático más estrictos, rigurosos y de tipo seguro, y los compiladores de Haskell generalmente borran todos los tipos. (Creo que la excepción es la aprobación de diccionarios de métodos para clases de tipos).

Jörg W Mittag
fuente
3
¡No! ¿Por qué? ¿Para qué se necesitaría esa información? El compilador genera el código para leer un charen el binario compilado. No emite el código para un int, no emite el código para un byte, no emite el código para un puntero, simplemente emite solo el código para a char. No se toman decisiones de tiempo de ejecución en función del tipo. No necesitas el tipo. Es completamente y completamente irrelevante. Todas las decisiones relevantes ya se han tomado en tiempo de compilación.
Jörg W Mittag
2
No hay El compilador simplemente pone código para imprimir un carácter en el binario. Período. El compilador sabe que en esa dirección de memoria hay char, por lo tanto, coloca el código para imprimir un char en el binario. Si el valor en esa dirección de memoria por alguna extraña razón no es un char, entonces, bueno, se desata el infierno. Así es básicamente cómo funciona toda una clase de exploits de seguridad.
Jörg W Mittag
2
Piénselo: si la CPU de alguna manera conociera los tipos de datos de los programas, entonces todos en el planeta tendrían que comprar una nueva CPU cada vez que alguien inventara un nuevo tipo. public class JoergsAwesomeNewType {};¿Ver? ¡Acabo de inventar un nuevo tipo! ¡Necesitas comprar una nueva CPU!
Jörg W Mittag
9
No. No lo hace. El compilador sabe qué código tiene que poner en el binario. No tiene sentido mantener esta información. Si está imprimiendo un int, el compilador colocará el código para imprimir un int. Si está imprimiendo un carácter, el compilador colocará el código para imprimir un carácter. Período. Pero es solo un poco patrón. El código para imprimir un carácter interpretará el patrón de bits de una manera determinada, el código para imprimir un int interpretará el bit de una manera diferente, pero no hay forma de distinguir un patrón de bits que sea un int de un patrón de bits que es un char, es una cadena de bits.
Jörg W Mittag
2
@ user16307: "¿El archivo exe no incluye información sobre qué dirección es qué tipo de datos?" Tal vez. Si compila con datos de depuración, los datos de depuración incluirán información sobre nombres de variables, direcciones y tipos. Y a veces esos datos de depuración se almacenan en el archivo .exe (como una secuencia binaria). Pero no es parte del código ejecutable, y no es utilizado por la aplicación en sí, solo por un depurador.
Ben Voigt
12

La computadora no "sabe" qué direcciones son qué, pero el conocimiento de qué es lo que se incluye en las instrucciones de su programa.

Cuando escribe un programa en C que escribe y lee una variable char, el compilador crea un código de ensamblaje que escribe ese dato en algún lugar como un char, y hay otro código en otro lugar que lee una dirección de memoria y lo interpreta como un char. Lo único que une estas dos operaciones es la ubicación de esa dirección de memoria.

Cuando llega el momento de leer, las instrucciones no dicen "ver qué tipo de datos hay", solo dice algo como "cargar esa memoria como flotante". Si la dirección de la que se va a leer se ha cambiado, o algo ha sobrescrito esa memoria con algo que no sea un flotante, la CPU cargará felizmente esa memoria como flotante, y como resultado pueden ocurrir todo tipo de cosas extrañas.

Mal tiempo de analogía: imagine un almacén de envío complicado, donde el almacén es memoria y las personas que recogen cosas es la CPU. Una parte del 'programa' del almacén coloca varios artículos en el estante. Otro programa va y toma artículos del almacén y los coloca en cajas. Cuando se retiran, no se comprueban, simplemente van a la papelera. Todo el almacén funciona con todo lo que funciona en sincronización, con los artículos correctos siempre en el lugar correcto en el momento correcto, de lo contrario, todo se bloquea, al igual que en un programa real.

whatsisname
fuente
¿Cómo explicaría si la CPU encuentra 0x00000061 en un registro y lo recupera? e imagine que se supone que el programa de consola genera esto como un carácter no int. ¿quiere decir que en ese archivo exe hay algunos códigos de instrucciones que saben que la dirección de 0x00000061 es un carácter y se convierte en un carácter mediante la tabla ASCII?
user16307
77
Tenga en cuenta que "todo se bloquea" es en realidad el mejor de los casos. "Suceden cosas extrañas" es el segundo mejor escenario, "suceden cosas sutilmente extrañas" es aún peor, y el peor de los casos es "las cosas suceden a sus espaldas que alguien manipuló intencionalmente para que ocurrieran de la manera que quieren". también conocido como una hazaña de seguridad.
Jörg W Mittag
@ user16307: El código en el programa le indicará a la computadora que busque esa dirección y luego la muestre de acuerdo con la codificación que se esté utilizando. Si los datos en la ubicación de la memoria son un carácter ASCII o basura completa, la computadora no está preocupada. Algo más fue responsable de configurar esa dirección de memoria para tener los valores esperados. Creo que podría beneficiarte probar alguna programación de ensamblaje.
whatsisname
1
@ JörgWMittag: de hecho. Pensé en mencionar un desbordamiento de búfer como ejemplo, pero decidí que solo haría las cosas más confusas.
whatsisname
@ user16307: Lo que muestra los datos en la pantalla es un programa. En unixen tradicional, es un terminal (una pieza de software que emula el terminal serie DEC VT100, un dispositivo de hardware con un monitor y un teclado que muestra todo lo que entra en su módem al monitor y envía lo que está escrito en su teclado a su módem). En DOS es DOS (en realidad, el modo de texto de su tarjeta VGA pero ignoremos eso) y en Windows es command.com. Su programa no sabe que en realidad está imprimiendo cadenas, solo está imprimiendo una secuencia de bytes (números).
slebetman
8

No lo hace. Una vez que C se compila en el código de máquina, la máquina solo ve un montón de bits. La forma en que se interpretan esos bits depende de qué operaciones se realicen en ellos en lugar de algunos metadatos adicionales.

Los tipos que ingresas en tu código fuente son solo para el compilador. Toma el tipo que usted dice que se supone que son los datos y, lo mejor que puede, trata de asegurarse de que esos datos solo se usen de manera que tengan sentido. Una vez que el compilador ha hecho un trabajo tan bueno como pudo al verificar la lógica de su código fuente, lo convierte en código de máquina y descarta los datos de tipo, porque el código de máquina no tiene forma de representar eso (al menos en la mayoría de las máquinas) .

8bittree
fuente
Lo que no entiendo es cómo sabe la computadora cuando lee el valor de una variable y una dirección como 10001 si es un int o char. Imagine que hago clic en un programa llamado anyprog.exe. Inmediatamente el código comienza a ejecutarse. ¿Este archivo exe incluye información sobre si las variables se almacenan como en o char? -
user16307
@ user16307 No, no hay información adicional sobre si algo es un int o un char. Agregaré algunas cosas de ejemplo más tarde, asumiendo que nadie más me gana.
8bittree
1
@ user16307: el archivo exe contiene esa información indirectamente. Al procesador que ejecuta el programa no le importan los tipos utilizados al escribir el programa, pero gran parte se puede deducir de las instrucciones utilizadas para acceder a las distintas ubicaciones de memoria.
Bart van Ingen Schenau
@ user16307 en realidad hay un poco de información adicional. Los archivos exe saben que un número entero es de 4 bytes, por lo que cuando escribe "int a", el compilador reserva 4 bytes para la variable a y, por lo tanto, puede calcular la dirección de ay las otras variables después.
Esben Skov Pedersen
1
@ user16307 no existe una diferencia práctica (además del tamaño del tipo) entre int a = 65y char b = 'A'una vez que se compila el código.
6

La mayoría de los procesadores proporcionan diferentes instrucciones para trabajar con datos de diferentes tipos, por lo que la información de tipo generalmente se "integra" en el código de máquina generado. No es necesario almacenar metadatos de tipo adicionales.

Algunos ejemplos concretos pueden ayudar. El siguiente código de máquina se generó usando gcc 4.1.2 en un sistema x86_64 que ejecuta SuSE Linux Enterprise Server (SLES) 10.

Asuma el siguiente código fuente:

int main( void )
{
  int x, y, z;

  x = 1;
  y = 2;

  z = x + y;

  return 0;
}

Aquí está la carne del código de ensamblaje generado correspondiente a la fuente anterior (usando gcc -S), con comentarios añadidos por mí:

main:
.LFB2:
        pushq   %rbp               ;; save the current frame pointer value
.LCFI0:
        movq    %rsp, %rbp         ;; make the current stack pointer value the new frame pointer value
.LCFI1:                            
        movl    $1, -12(%rbp)      ;; x = 1
        movl    $2, -8(%rbp)       ;; y = 2
        movl    -8(%rbp), %eax     ;; copy the value of y to the eax register
        addl    -12(%rbp), %eax    ;; add the value of x to the eax register
        movl    %eax, -4(%rbp)     ;; copy the value in eax to z
        movl    $0, %eax           ;; eax gets the return value of the function
        leave                      ;; exit and restore the stack
        ret

Hay algunas cosas adicionales que siguen ret, pero no es relevante para la discusión.

%eaxes un registro de datos de propósito general de 32 bits. %rspes un registro de 64 bits reservado para guardar el puntero de la pila , que contiene la dirección de lo último que se introdujo en la pila. %rbpes un registro de 64 bits reservado para guardar el puntero de cuadro , que contiene la dirección del cuadro de pila actual . Un marco de pila se crea en la pila cuando ingresa una función, y reserva espacio para los argumentos de la función y las variables locales. Se accede a los argumentos y las variables mediante el uso de desplazamientos desde el puntero del marco. En este caso, la memoria para la variable xes 12 bytes "debajo" de la dirección almacenada %rbp.

En el código anterior, copiamos el valor entero de x(1, almacenado en -12(%rbp)) en el registro %eaxutilizando la movlinstrucción, que se utiliza para copiar palabras de 32 bits de una ubicación a otra. Luego llamamos addl, que agrega el valor entero de y(almacenado en -8(%rbp)) al valor que ya está en %eax. Luego guardamos el resultado en -4(%rbp), que es z.

Ahora cambiemos eso para que tratemos con doublevalores en lugar de intvalores:

int main( void )
{
  double x, y, z;

  x = 1;
  y = 2;

  z = x + y;

  return 0;
}

Correr gcc -Snuevamente nos da:

main:
.LFB2:
        pushq   %rbp                              
.LCFI0:
        movq    %rsp, %rbp
.LCFI1:
        movabsq $4607182418800017408, %rax ;; copy literal 64-bit floating-point representation of 1.00 to rax
        movq    %rax, -24(%rbp)            ;; save rax to x
        movabsq $4611686018427387904, %rax ;; copy literal 64-bit floating-point representation of 2.00 to rax
        movq    %rax, -16(%rbp)            ;; save rax to y
        movsd   -24(%rbp), %xmm0           ;; copy value of x to xmm0 register
        addsd   -16(%rbp), %xmm0           ;; add value of y to xmm0 register
        movsd   %xmm0, -8(%rbp)            ;; save result to z
        movl    $0, %eax                   ;; eax gets return value of function
        leave                              ;; exit and restore the stack
        ret

Varias diferencias En lugar de movly addl, usamos movsdy addsd(asignamos y agregamos flotantes de doble precisión). En lugar de almacenar valores provisionales %eax, usamos %xmm0.

Esto es lo que quiero decir cuando digo que el tipo está "integrado" en el código de la máquina. El compilador simplemente genera el código de máquina correcto para manejar ese tipo particular.

John Bode
fuente
4

Históricamente , C consideraba que la memoria consistía en varios grupos de ranuras numeradas de tipounsigned char(también llamado "byte", aunque no siempre tiene que ser de 8 bits). Cualquier código que usara cualquier cosa almacenada en la memoria necesitaría saber en qué ranura o ranuras se almacenó la información, y saber qué se debe hacer con la información allí [por ejemplo, interprete los cuatro bytes que comienzan en la dirección 123: 456 como un código de 32 bits el valor de punto flotante "o" almacena los 16 bits inferiores de la cantidad calculada más recientemente en dos bytes comenzando en la dirección 345: 678]. La memoria en sí misma no sabría ni importaría lo que significaban los valores almacenados en las ranuras de memoria ". Si El código intentó escribir memoria usando un tipo y leerlo como otro, los patrones de bits almacenados por la escritura se interpretarían de acuerdo con las reglas del segundo tipo, con las consecuencias que pudieran resultar.

Por ejemplo, si el código se almacenara 0x12345678en un valor de 32 bits unsigned inty luego intentara leer dos unsigned intvalores consecutivos de 16 bits de su dirección y el de arriba, entonces, dependiendo de qué mitad del unsigned intalmacenamiento estaba donde, el código podría leer los valores 0x1234 y 0x5678, o 0x5678 y 0x1234.

Sin embargo, el estándar C99 ya no requiere que la memoria se comporte como un grupo de ranuras numeradas que no saben nada sobre lo que representan sus patrones de bits . Se permite que un compilador se comporte como si las ranuras de memoria conocieran los tipos de datos que están almacenados en ellos, y solo permitirá que los datos que se escriben usando cualquier otro tipo que no unsigned charsean leídos usando el tipo unsigned charo el mismo tipo que fueron escritos con; Además, los compiladores pueden comportarse como si las ranuras de memoria tuvieran el poder y la inclinación de corromper arbitrariamente el comportamiento de cualquier programa que intente acceder a la memoria de una manera contraria a esas reglas.

Dado:

unsigned int a = 0x12345678;
unsigned short p = (unsigned short *)&a;
printf("0x%04X",*p);

algunas implementaciones pueden imprimir 0x1234, y otras pueden imprimir 0x5678, pero bajo el Estándar C99 sería legal que una implementación imprima "¡REGLAS FRINK!" o hacer cualquier otra cosa, bajo la teoría de que sería legal que las ubicaciones de memoria acontengan hardware que registre qué tipo se usó para escribirlas, y que dicho hardware responda a un intento de lectura no válido de cualquier manera, incluso causando "¡REGLAS MALDITAS!" para ser salida.

Tenga en cuenta que no importa si realmente existe tal hardware: el hecho de que dicho hardware pueda existir legalmente hace que sea legal para los compiladores generar código que se comporta como si se estuviera ejecutando en dicho sistema. Si el compilador puede determinar que una ubicación de memoria en particular se escribirá como un tipo y se leerá como otro, puede pretender que se está ejecutando en un sistema cuyo hardware podría hacer tal determinación y podría responder con cualquier grado de capricho que el autor del compilador considere apropiado. .

El propósito de esta regla era permitir a los compiladores que sabían que un grupo de bytes que contenía un valor de algún tipo contenía un valor particular en algún momento, y que desde entonces no se había escrito ningún valor del mismo tipo, para inferir que ese grupo de bytes aún mantendría ese valor. Por ejemplo, un procesador había leído un grupo de bytes en un registro, y luego quería usar la misma información nuevamente mientras aún estaba en el registro, el compilador podía usar el contenido del registro sin tener que releer el valor de la memoria. Una optimización útil. Durante los primeros diez años de la regla, violarla generalmente significaría que si una variable se escribe con un tipo diferente al que se usa para leerla, la escritura puede o no afectar el valor leído. Tal comportamiento puede en algunos casos ser desastroso, pero en otros casos puede ser inofensivo,

Sin embargo, alrededor de 2009, los autores de algunos compiladores como CLANG han determinado que, dado que el Estándar permite a los compiladores hacer lo que quieran en los casos en que la memoria se escribe usando un tipo y se lee como otro, los compiladores deben inferir que los programas nunca recibirán información que pueda hacer que tal cosa ocurra. Dado que el Estándar dice que el compilador puede hacer lo que quiera cuando se recibe dicha entrada inválida, el código que solo tendría un efecto en los casos en que el Estándar no impone requisitos puede (y en opinión de algunos autores del compilador) debería omitirse como irrelevante Esto cambia el comportamiento de las violaciones de alias de ser como la memoria que, dada una solicitud de lectura, puede devolver arbitrariamente el último valor escrito usando el mismo tipo que una solicitud de lectura o cualquier valor más reciente escrito usando algún otro tipo,

Super gato
fuente
1
Mencionar el comportamiento indefinido cuando se realiza la poda tipográfica a alguien que no entiende cómo no hay RTTI parece contrario a la intuición
Cole Johnson
@ColeJohnson: Es una lástima que no haya un nombre formal o estándar para el dialecto de C compatible con el 99% de los compiladores anteriores a 2009, ya que tanto desde una perspectiva docente como práctica deberían considerarse lenguajes fundamentalmente diferentes. Dado que se le da el mismo nombre al dialecto que desarrolló una serie de comportamientos predecibles y optimizables a lo largo de 35 años, el dialecto que arroja tales comportamientos con el supuesto propósito de optimización, es difícil evitar la confusión cuando se habla de cosas que funcionan de manera diferente en ellos .
supercat
Históricamente, C se ejecutaba en las máquinas Lisp que no permitían jugar tan libremente con los tipos. Estoy bastante seguro de que muchos de los "comportamientos predecibles y optimizables" vistos hace 30 años simplemente no funcionaron en ningún otro lado que BSD Unix en el VAX.
prosfilaes
@prosfilaes: ¿Quizás "99% de los compiladores que se usaron entre 1999 y 2009" serían más precisos? Incluso cuando los compiladores tenían opciones para algunas optimizaciones enteras bastante agresivas, eran solo eso: opciones. No sé si he visto un compilador antes de 1999 que no tuviera un modo que no garantizara que dada int x,y,z;la expresión x*y > znunca haría otra cosa que devolver 1 o 0, o donde las violaciones de alias tendrían algún efecto aparte de permitir que el compilador devuelva arbitrariamente un valor antiguo o nuevo.
supercat
1
... donde los unsigned charvalores que se utilizan para construir un tipo "provienen". Si un programa descomponga un puntero en un unsigned char[], muestre brevemente su contenido hexadecimal en la pantalla y luego borre el puntero unsigned char[], y luego acepte algunos números hexadecimales del teclado, cópielos de nuevo en un puntero y luego desreferencia ese puntero , el comportamiento estaría bien definido en el caso en que el número que se escribió coincidiera con el número que se mostró.
supercat
3

En C, no lo es. Otros lenguajes (por ejemplo, Lisp, Python) tienen tipos dinámicos, pero C está tipado estáticamente. Eso significa que su programa debe saber qué tipo de datos deben interpretar correctamente es un carácter, un entero, etc.

Por lo general, el compilador se encarga de esto por usted, y si hace algo mal, obtendrá un error (o advertencia) en tiempo de compilación.

Mike Harris
fuente
Lo que no entiendo es cómo sabe la computadora cuando lee el valor de una variable y una dirección como 10001 si es un int o char. Imagine que hago clic en un programa llamado anyprog.exe. Inmediatamente el código comienza a ejecutarse. ¿Este archivo exe incluye información sobre si las variables se almacenan como en o char? -
user16307
1
@ user16307 Esencialmente no, toda esa información se pierde por completo. Depende del código de la máquina estar diseñado lo suficientemente bien como para hacer su trabajo correctamente, incluso sin esa información. Lo único que le importa a la computadora es que haya ocho bits seguidos en la dirección 10001. Es su trabajo o el trabajo del compilador , dependiendo del caso, mantenerse al día con cosas como esas manualmente mientras escribe la máquina o el código de ensamblaje.
Panzercrisis
1
Tenga en cuenta que la escritura dinámica no es la única razón para retener los tipos. Java está estáticamente tipado, pero aún debe retener los tipos, ya que permite reflexionar dinámicamente sobre el tipo. Además, tiene polimorfismo de tiempo de ejecución, es decir, envío de método basado en el tipo de tiempo de ejecución, para el cual también necesita el tipo. C ++ coloca el código de envío del método en el objeto (o más bien en la clase), por lo que no necesita el tipo en cierto sentido (aunque, por supuesto, la tabla vtable es, en cierto sentido, parte del tipo, así que, al menos en parte el tipo se conserva), pero en Java, el código de envío del método está centralizado.
Jörg W Mittag
mira mi pregunta que escribí "cuando se ejecuta un programa C?" ¿no se almacenan indirectamente en un archivo exe entre códigos de instrucciones y eventualmente ocupan lugares en la memoria? Escribo esto nuevamente para usted: si la CPU encuentra 0x00000061 en un registro y lo busca; e imagine que se supone que el programa de consola genera esto como un carácter no int. ¿hay en ese archivo exe (máquina / código binario) algunos códigos de instrucciones que sepan que la dirección de 0x00000061 es un carácter y se convierte en un carácter mediante la tabla ASCII? Si es así, ¿los identificadores char int están indirectamente en el binario?
user16307
Si el valor es 0x61 y se declara como un carácter (es decir, 'a') y llama a una rutina para mostrarlo, [eventualmente] habrá una llamada al sistema para mostrar ese carácter. Si lo ha declarado como int y llama a la rutina de visualización, el compilador sabrá generar código para convertir 0x61 (decimal 97) a la secuencia ASCII 0x39, 0x37 ('9', '7'). En pocas palabras: el código que se genera es diferente porque el compilador sabe tratarlos de manera diferente.
Mike Harris
3

Tienes que distinguir entre compiletimey runtimepor un lado codey datapor otro.

Desde la perspectiva de la máquina, no hay diferencia entre lo que llamas codeo instructionslo que llamas data. Todo se reduce a números. Pero algunas secuencias, lo que llamaríamos code, hacen algo que encontramos útil, otras simplemente crashla máquina.

El trabajo que realiza la CPU es un simple bucle de 4 pasos:

  • Obtener "datos" de una dirección determinada
  • Decodifica la instrucción (es decir, "interpreta" el número como un instruction)
  • Leer una dirección efectiva
  • Ejecutar y almacenar resultados

Esto se llama ciclo de instrucción .

Leí que A y 4 están almacenados en direcciones RAM aquí. ¿Pero qué hay de a y x?

ay xson variables, que son marcadores de posición para las direcciones, donde el programa podría encontrar el "contenido" de las variables. Entonces, cada vez que ase usa la variable , existe efectivamente la dirección del contenido de ausado.

Más confuso, ¿cómo sabe la ejecución que a es char y x es int?

La ejecución no sabe nada. Por lo que se dijo en la introducción, la CPU solo obtiene datos e interpreta estos datos como instrucciones.

La función printf está diseñada para "saber", qué tipo de entrada está ingresando, es decir, su código resultante proporciona las instrucciones correctas sobre cómo manejar un segmento de memoria especial. Por supuesto, es posible generar una salida sin sentido: el uso de una dirección, donde no se almacena ninguna cadena junto con "% s" printf()dará como resultado una salida sin sentido detenida solo por una ubicación de memoria aleatoria, donde está un 0 ( \0).

Lo mismo ocurre con el punto de entrada de un programa. Bajo el C64 fue posible poner sus programas en (casi) todas las direcciones conocidas. Los programas de ensamblaje se iniciaron con una instrucción llamada sysseguida de una dirección: sys 49152era un lugar común para colocar su código de ensamblador. Pero nada le impedía cargar, por ejemplo, datos gráficos 49152, lo que resulta en un bloqueo de la máquina después de "comenzar" desde este punto. En este caso, el ciclo de instrucción comenzó con la lectura de "datos gráficos" y tratando de interpretarlo como "código" (que por supuesto no tenía sentido); los efectos fueron a veces asombrosos;)

Digamos que un valor se almacena en algún lugar de la RAM como 10011001; si soy el programa que ejecuta el código, ¿cómo sabré si este 10011001 es un char o un int?

Como se dijo: el "contexto", es decir, las instrucciones anteriores y siguientes, ayuda a tratar los datos de la manera que queremos. Desde la perspectiva de la máquina, no hay diferencia en ninguna ubicación de memoria. inty chares solo vocabulario, que tiene sentido en compiletime; durante runtime(en un nivel de ensamblaje), no hay charo int.

Lo que no entiendo es cómo sabe la computadora, cuando lee el valor de una variable de una dirección como 10001, ya sea int o char.

La computadora no sabe nada. El programador lo hace. El código compilado genera el contexto , que es necesario para generar resultados significativos para los humanos.

¿Este archivo ejecutable incluye información sobre si las variables almacenadas son del tipo int o char

y no . La información, ya sea una into una charse pierde. Pero, por otro lado, se conserva el contexto (las instrucciones que indican cómo manejar las ubicaciones de memoria, dónde se almacenan los datos); por lo que implícitamente sí, la "información" es implícitamente disponible.

Thomas Junk
fuente
Bonita distinción entre tiempo de compilación y tiempo de ejecución.
Michael Blackburn
2

Mantengamos esta discusión solo en lenguaje C.

El programa al que se refiere está escrito en un lenguaje de alto nivel como C. La computadora solo entiende el lenguaje de máquina. Los lenguajes de nivel superior le dan al programador la capacidad de expresar la lógica de una manera más amigable para los humanos, que luego se traduce en código de máquina que el microprocesador puede decodificar y ejecutar. Ahora déjenos discutir el código que mencionó:

char a = 'A';
int x = 4;

Tratemos de analizar cada parte:

char / int se conocen como tipos de datos. Estos le dicen al compilador que asigne memoria. En el caso de charque sea 1 byte y int2 bytes. (Tenga en cuenta que este tamaño de memoria depende nuevamente del microprocesador).

a / x se conocen como identificadores. Ahora, estos son los nombres "fáciles de usar" que se dan a las ubicaciones de memoria en la RAM.

= le dice al compilador que almacene 'A' en la ubicación de la memoria ay 4 en la ubicación de la memoria x.

Por lo tanto, los identificadores de tipo de datos int / char solo los utiliza el compilador y no el microprocesador durante la ejecución del programa. Por lo tanto, no se almacenan en la memoria.

prasad
fuente
ok Los identificadores de tipo de datos int / char no se almacenan directamente en la memoria como variables, pero ¿no se almacenan indirectamente en un archivo exe entre los códigos de instrucción y eventualmente tienen lugar en la memoria? Escribo esto nuevamente para usted: si la CPU encuentra 0x00000061 en un registro y lo busca; e imagine que se supone que el programa de consola genera esto como un carácter no int. ¿hay en ese archivo exe (máquina / código binario) algunos códigos de instrucciones que sepan que la dirección de 0x00000061 es un carácter y se convierte en un carácter mediante la tabla ASCII? Si es así, ¿los identificadores char int están indirectamente en el binario?
user16307
No para CPU todos sus números. Para su ejemplo específico, la impresión en la consola no depende de si la variable es char o int. Actualizaré mi respuesta con un flujo detallado de cómo el programa de alto nivel se convierte en lenguaje de máquina hasta la ejecución del programa.
prasad
2

Mi respuesta aquí es algo simplificada y se referirá solo a C.

No, la información de tipo no se almacena en el programa.

into charno son indicadores de tipo para la CPU; solo al compilador.

El exe creado por el compilador tendrá instrucciones para manipular ints si la variable fue declarada como un int. Del mismo modo, si la variable se declaró como a char, el exe contendrá instrucciones para manipular a char.

Cía:

int main()
{
    int a = 65;
    char b = 'A';
    if(a == b)
    {
        printf("Well, what do you know. A char can equal an int.\n");
    }
    return 0;
}

Este programa imprimirá su mensaje, ya que chary inttienen los mismos valores en RAM.

Ahora, si se está preguntando cómo se las printfarregla para generar 65una inty Apara una char, es porque debe especificar en la "cadena de formato" cómo printfdebe tratar el valor .
(Por ejemplo, %csignifica tratar el valor como a char, y %dsignifica tratar el valor como un entero; sin embargo, el mismo valor en ambos sentidos).

BenjiWiebe
fuente
2
Esperaba que alguien usara un ejemplo usando printf. @OP: int a = 65; printf("%c", a)saldrá 'A'. ¿Por qué? Porque al procesador no le importa. Para ello, todo lo que ve son bits. Su programa le dijo al procesador que almacenara 65 (casualmente el valor de 'A'en ASCII) en ay luego mostrara un carácter, lo cual con gusto lo hace. ¿Por qué? Porque no le importa
Cole Johnson
pero ¿por qué algunos dicen aquí en el caso de C #, no es la historia? Leí algunos otros comentarios y dicen que en C # y C ++ la historia (información sobre los tipos de datos) es diferente e incluso la CPU no hace la computación. ¿Alguna idea sobre eso?
user16307
@ user16307 Si la CPU no hace la computación, el programa no se está ejecutando. :) En cuanto a C #, no lo sé, pero creo que mi respuesta también se aplica allí. En cuanto a C ++, sé que mi respuesta se aplica allí.
BenjiWiebe
0

En el nivel más bajo, en la CPU física real no hay ningún tipo (ignorando las unidades de coma flotante). Solo patrones de bits. Una computadora funciona manipulando patrones de bits, muy, muy rápido.

Eso es todo lo que la CPU hace, todo lo que puede hacer. No hay tal cosa como un int o un char.

x = 4 + 5

Se ejecutará como:

  1. Cargue 00000100 en el registro 1
  2. Cargue 00000101 en el registro 2
  3. IAgregue el registro 1 para registrar 2, y almacene en el registro 1

La instrucción iadd activa el hardware que se comporta como si los registros 1 y 2 fueran enteros. Si en realidad no representan números enteros, todo tipo de cosas pueden salir mal más tarde. El mejor resultado suele ser estrellarse.

Está en el compilador elegir la instrucción correcta en función de los tipos proporcionados en la fuente, pero en el código de máquina real ejecutado por la CPU, no hay tipos, en ninguna parte.

editar: Tenga en cuenta que el código de máquina real no menciona de hecho 4, 5 o entero en ninguna parte. son solo dos patrones de bits, y una instrucción que toma dos patrones de bits, asume que son ints y los suma.

Leliel
fuente
0

Respuesta corta, el tipo está codificado en las instrucciones de la CPU que genera el compilador.

Aunque la información sobre el tipo o el tamaño de la información no se almacena directamente, el compilador realiza un seguimiento de esta información al acceder, modificar y almacenar valores en estas variables.

¿Cómo sabe la ejecución que a es un char y x es un int?

No lo hace, pero cuando el compilador produce el código de la máquina, lo sabe. An inty a charpueden ser de diferentes tamaños. En una arquitectura donde un carácter es del tamaño de un byte y un int es de 4 bytes, entonces la variable xno está en la dirección 10001, sino también en 10002, 10003 y 10004. Cuando el código necesita cargar el valor xen un registro de CPU, Utiliza las instrucciones para cargar 4 bytes. Al cargar un char, utiliza las instrucciones para cargar 1 byte.

¿Cómo elegir cuál de las dos instrucciones? El compilador decide durante la compilación, no se realiza en tiempo de ejecución después de inspeccionar los valores en la memoria.

Tenga en cuenta también que los registros pueden ser de diferentes tamaños. En las CPU Intel x86, el EAX tiene 32 bits de ancho, la mitad es AX, que es 16, y AX se divide en AH y AL, ambos de 8 bits.

Entonces, si desea cargar un número entero (en CPU x86), use la instrucción MOV para enteros, para cargar un carácter use la instrucción MOV para caracteres. Ambos se llaman MOV, pero tienen diferentes códigos operativos. Efectivamente siendo dos instrucciones diferentes. El tipo de la variable está codificado en las instrucciones de uso.

Lo mismo sucede con otras operaciones. Existen muchas instrucciones para realizar la suma, según el tamaño de los operandos, e incluso si están firmados o no. Consulte https://en.wikipedia.org/wiki/ADD_(x86_instruction) que enumera las diferentes adiciones posibles.

Digamos que un valor se almacena en algún lugar de la RAM como 10011001; si soy el programa que ejecuta el código, ¿cómo sabré si este 10011001 es un char o un int

Primero, un char sería 10011001, pero un int sería 00000000 00000000 00000000 10011001, porque son de diferentes tamaños (en una computadora con los mismos tamaños mencionados anteriormente). Pero vamos a considerar el caso para signed charvs unsigned char.

Lo que se almacena en una ubicación de memoria se puede interpretar de la forma que desee. Parte de las responsabilidades del compilador de C es garantizar que lo que se almacena y lee de una variable se realiza de manera coherente. Entonces, no es que el programa sepa lo que está almacenado en una ubicación de memoria, sino que acepta de antemano que siempre leerá y escribirá el mismo tipo de cosas allí. (sin contar cosas como los tipos de casting).

frozenkoi
fuente
pero ¿por qué algunos dicen aquí en el caso de C #, no es la historia? Leí algunos otros comentarios y dicen que en C # y C ++ la historia (información sobre los tipos de datos) es diferente e incluso la CPU no hace la computación. ¿Alguna idea sobre eso?
user16307
0

pero ¿por qué algunos dicen aquí en el caso de C #, no es la historia? Leí algunos otros comentarios y dicen en C # y C ++ que la historia (información sobre los tipos de datos) es diferente e incluso la CPU no hace la computación. ¿Alguna idea sobre eso?

En lenguajes de verificación de tipos como C #, la compilación realiza la verificación de tipos. El código benji escribió:

int main()
{
    int a = 65;
    char b = 'A';
    if(a == b)
    {
        printf("Well, what do you know. A char can equal an int.\n");
    }
    return 0;
}

Simplemente se negaría a compilar. De manera similar, si intentas multiplicar una cadena y un número entero (iba a decir agregar, pero el operador '+' está sobrecargado con concatenación de cadenas y podría funcionar).

int a = 42;
string b = "Compilers are awesome.";
double[] c = a * b;

El compilador simplemente se negaría a generar código de máquina a partir de este C #, sin importar cuánto se haya besado su cadena.

Michael Blackburn
fuente
-4

Las otras respuestas son correctas, ya que esencialmente todos los dispositivos de consumo que encontrará no almacenan información de tipo. Sin embargo, ha habido varios diseños de hardware en el pasado (y en la actualidad, en un contexto de investigación) que usan una arquitectura etiquetada : almacenan tanto los datos como el tipo (y posiblemente también otra información). Estos incluirían más prominentemente las máquinas Lisp .

Recuerdo vagamente haber escuchado sobre una arquitectura de hardware diseñada para programación orientada a objetos que tenía algo similar, pero ahora no puedo encontrarla.

Nathan Ringo
fuente
3
La pregunta establece específicamente que se refiere al lenguaje C (no a Lisp), y el lenguaje C no almacena metadatos variables. Si bien es posible que una implementación de C haga esto, ya que el estándar no lo prohíbe, en la práctica nunca sucede. Si usted tiene ejemplos pertinentes a la pregunta, por favor proporcione citas específicas y proporcionar referencias que se relacionan con el lenguaje C .
Bueno, podrías escribir un compilador de C para una máquina Lisp, pero nadie usa máquinas Lisp en la actualidad en general. La arquitectura orientada a objetos era Rekursiv , por cierto.
Nathan Ringo
2
Creo que esta respuesta no es útil. Complica las cosas mucho más allá del nivel actual de comprensión de la OP. Está claro que el OP no entiende el modelo de ejecución básico de una CPU + RAM, y cómo un compilador traduce la fuente simbólica de alto nivel a un binario ejecutable. La memoria etiquetada, RTTI, Lisp, etc., está mucho más allá de lo que el autor de la pregunta necesita saber en mi opinión, y solo lo confundirá más.
Andres F.
pero ¿por qué algunos dicen aquí en el caso de C #, no es la historia? Leí algunos otros comentarios y dicen que en C # y C ++ la historia (información sobre los tipos de datos) es diferente e incluso la CPU no hace la computación. ¿Alguna idea sobre eso?
user16307