Como um computador determina o tipo de dados de um byte?

31

Por exemplo, se o computador 10111100armazenou em um byte específico de RAM, como o computador sabe interpretar esse byte como um número inteiro, caractere ASCII ou algo mais? Os dados de tipo são armazenados em um byte adjacente? (Eu não acho que seria esse o caso, pois isso resultaria no uso do dobro da quantidade de espaço para um byte.)

Suspeito que talvez um computador nem saiba o tipo de dados, que apenas o programa que o utiliza saiba. Meu palpite é que, como a RAM é R AM e, portanto, não é lida seqüencialmente, um programa específico diz à CPU para buscar as informações de um endereço específico e o programa define como tratá-las. Isso parece se encaixar com coisas de programação, como a necessidade de conversão de tipo.

Estou no caminho certo?

Bassinator
fonte
4
Como uma observação lateral: se você está falando sobre tipos, deve fazê-lo em um contexto de linguagem. É deixado para o compilador lidar com esse tipo de coisa (símbolos, tipos de verificação, operações, conversão, ram de endereço, etc.). CPU e RAM conhecem apenas bytes
jean
4
O tipo de dados de um byte é um byte. Além disso, o computador não sabe de nada. Um programa pode interpretar um byte ou um grupo de bytes como um tipo de dados específico e tentar executar operações nesses, mas não há restrições. O mesmo grupo de bytes pode ser interpretado como mais de um tipo de dados (por exemplo, converter ponteiros para tipos de valor, uniões do tipo C etc.). O fato de a RAM não ser lida sequencialmente não é realmente relevante. - É mais porque a RAM é de uso geral. - Registros, por exemplo, também não são lidos sequencialmente, mas são digitados.
BrainSlugs83
5
Plug vergonhoso para mim, mas essa pergunta foi basicamente feita nos programadores SE cerca de um mês atrás. Aqui está a minha resposta . É meio que longo neste momento, mas ataca de vários ângulos diferentes.
Shaz
2
Uma conseqüência útil do fato de o hardware ser independente de tipo de dados é que um único byte (ou palavra etc.) pode ser interpretado de várias maneiras por um programa. Notavelmente, a interpretação temporária de um número de ponto flutuante como um número inteiro é usada para calcular a raiz quadrada inversa rápida .
Aoeuid 03/09/2015
@ BrainSlugs83, considere converter isso em uma resposta?
DW

Respostas:

38

Sua suspeita está correta. A CPU não se importa com a semântica dos seus dados. Às vezes, porém, faz diferença. Por exemplo, algumas operações aritméticas produzem resultados diferentes quando os argumentos são semanticamente assinados ou não assinados. Nesse caso, você precisa informar à CPU qual interpretação você pretendeu.

Cabe ao programador entender seus dados. A CPU apenas obedece às ordens, alegremente inconscientes de seus significados ou objetivos.

Yuval Filmus
fonte
1
Em relação a "quando os argumentos são semanticamente assinados ou não assinados", como a CPU saberia? As operações da CPU apenas veem os bytes de parâmetro e não têm esse tipo de reconhecimento de contexto do tipo de dados. Você implica o tipo de dados escolhendo a operação apropriada da CPU (ou o seu compilador faz).
Faca
4
@Shiv Nesses casos, a CPU recebe uma instrução diferente para processar números assinados versus números não assinados. Como nas suspeitas do OP, o programa é obrigado a fornecer esses detalhes, porque a CPU não tem conhecimento.
Cort Ammon - Restabelece Monica
2
Estou trabalhando com computadores desde que me lembro de mim mesmo, e mesmo sabendo que a CPU não se importa com as construções de alto nível que usamos na programação de alto nível, mas essa separação de conceitos ainda me assusta de vez em quando
Loupax
1
@Loupax Bem, trabalhar com uma montagem realmente de baixo nível ajuda bastante - até mov al, 42é um pouco de alto nível - é óbvio que há apenas uma instrução possível que isso poderia chamar, mas ainda é um pouco abstraída. No entanto, usando mov.8 al, 42faz explicitamente essa :) dolorosamente óbvio
Luaan
1
@ Shiv: Eu gostaria de observar que existem máquinas onde os dados na memória são digitados. Eles são chamados de arquiteturas de memória com tags (ou simplesmente arquiteturas com tags), mas não tiveram tanto sucesso comercial quanto as arquiteturas regulares, em parte porque agora programamos principalmente em linguagens compiladas, em vez de assembly, e o compilador cuida da digitação. Veja: en.wikipedia.org/wiki/Tagged_architecture
slebetman
14

Como outros já responderam, as CPUs comuns de hoje não sabem o que uma determinada posição de memória contém; o software decide.

No entanto, existem outras possibilidades. As máquinas Lisp, por exemplo, usavam uma arquitetura marcada que armazenava o tipo de cada posição de memória; dessa maneira, o próprio hardware poderia fazer parte do trabalho de linguagens de alto nível.

E mesmo agora, acho que você pode considerar o bit NX na Intel, AMD, ARM e outras arquiteturas como seguindo o mesmo princípio: distinguir no nível do hardware se uma determinada zona de memória contém dados ou instruções.

Além disso, apenas para ser completo, nas arquiteturas de Harvard (como alguns microcontroladores), os dados e as instruções são fisicamente separados, para que a CPU tenha alguma idéia do que está lendo.

Nesta pergunta do Quora, há alguns comentários sobre como a memória marcada funcionou, suas implicações e queda de desempenho e muito mais.

hmijail
fonte
A arquitetura com tags é uma observação interessante. Seria significativamente mais rápido?
Bassinator 22/03
4

Sim. O programa apenas obtém um byte da memória e pode interpretá-lo como quiser.

David Richerby
fonte
3

Não há anotações de tipo.
A RAM armazena dados puros e o programa define o que fazer.

Com os registros de CPU é um pouco mais difícil, se você tiver registros de um determinado tipo (como FPU), será informado o que está dentro.
Operações em registros de ponto flutuante estão explicitamente usando dados digitados. Você ou seu compilador diz o que e quando deve ser colocado lá, para que você não tenha essa liberdade.
O computador não faz nenhuma suposição sobre os dados subjacentes na RAM e, nos registros com uma exceção, os registros na CPU são do tipo conhecido, otimizados para lidar com isso. Isso é apenas para mostrar que há lugares em que os dados devem ser do tipo esperado, mas nada impede que você projete seqüências de caracteres para flutuações e multiplique-as.

Nas linguagens de programação, você especifica o tipo ou, nas linguagens de nível superior, os dados são gerais e o compilador / intérprete / VM codifica o que há dentro da sobrecarga.
Por exemplo, em C, seu tipo de ponteiro diz o que fazer com os dados, como acessá-los.

Claro que você pode ler string (caracteres) e então tratar como valores de ponto flutuante, números inteiros e misturá-los.

Mal
fonte
Mesmo os bits em um registro FPU nem sempre representam valores de ponto flutuante. Antigamente (talvez não tanto mais?), Uma otimização comum era usar registros de ponto flutuante (64 bits ou mais) para copiar dados mais rapidamente do que registros de uso geral / número inteiro (32 bits), sendo duas vezes maior, eles geralmente conseguiam copiar dados duas vezes mais rápido.
Seth
1
Eu concordo totalmente com você, é por isso que escrevi que alguém poderia empurrar as cordas para lá. E, ao mesmo tempo, as pessoas faziam operações de ponto flutuante em números inteiros, porque era mais rápido. Esse é o ponto!
Mal
@HCBPshenanigans, existem instruções que manipulam valores de ponto flutuante. Se o FADD for usado, faz sentido que os grupos de memória (4,8 ou 10) de bytes contenham números de ponto flutuante. Isso vale para vários tipos de instrução: multiplicar dois números inteiros só faz sentido se forem inteiros, salto apenas faz sentido se for um endereço.
JDługosz 04/09/2015
@seth and evilJS que não é considerado o caso das instruções 8087 empilhadas de ponto flutuante herdado, mas é o caso dos registros CIMD mais recentes que podem ser usados ​​apenas para carregar / salvar sem interpretação (embora eles devam estar alinhados) e uma ressalva que, se os registros CIMD nunca foram usados, eles não precisam ser salvos em uma alternância de contexto. Se você (somente) mover 8 bytes via registro XMM, será uma perda líquida, pois todo o conjunto precisa ser salvo.
JDługosz 04/09/2015
3

A CPU não se importa, ele executa o código de montagem, que apenas move os dados, muda, adiciona ou multiplica os dados ...

Tipos de dados são um conceito de linguagem de nível superior: em C ou C ++, é necessário especificar tipos para cada dado que você manipula; o compilador C / C ++ cuida da transformação desses dados nos comandos certos para a CPU processar (os compiladores escrevem o código do assembly)

Em algumas linguagens de nível ainda mais alto, Types pode ser inferido: em Python ou Javascript, por exemplo, não é necessário especificar tipos de dados, mas os dados têm um tipo e você não pode adicionar uma string com um número inteiro, mas pode adicionar um float com um número inteiro: o 'compilador' (que no caso do Javascript é um compilador JIT (Just in Time). O Javascript é frequentemente chamado de linguagem 'interpretada' porque os navegadores historicamente interpretavam o código Javascript, mas atualmente os mecanismos Javascript são compiladores.

Código, sempre acaba sendo compilado no código da máquina, mas obviamente o formato do código da máquina depende da máquina que você está alvejando (o código x86 de 64 bits não funcionará em uma máquina x86 de 32 bits ou em um processador ARM, por exemplo)

Portanto, há muitas camadas envolvidas na execução de código interpretado.

Java e C # são outros interessantes, pois o código Java ou C # é tecnicamente 'compilado' em um binário Java (bytecode), mas esse código é interpretado pelo Java Runtime, que é específico ao hardware subjacente (é necessário instalar o JRE visando a máquina certa para executar binários Java (Jars))

MrE
fonte
Um compilador compila, seja JIT ou não; e um intérprete interpreta sem compilar (porque, se não, seria um compilador!). São coisas muito diferentes. E em relação a "Java ser engraçado" por causa da interpretação do bytecode, considere que mesmo o código da máquina x86 será realmente interpretado (ou mesmo compilado?) Pelo próprio microprocessador em microcódigo .
hmijail
Obrigado pelo esclarecimento ... Concordado: um compilador compila e um intérprete interpreta. No caso do Javascript, a história é um pouco complicada, pois alguns navegadores mais antigos interpretam o código, enquanto navegadores mais modernos compilam just-in-time, e é provavelmente por isso que ainda é referida como uma linguagem 'interpretada', embora tecnicamente não é mais.
22417 MrE
Mas o AFAIK, JS começa a ser interpretado e pode ser compilado conforme necessário. E os JITs podem mudar de interpretado para compilado para interpretado novamente, dependendo de muitas coisas. Por exemplo, um pedaço de código pode ser compilado para uma variável com um determinado tipo; mas, em seguida, o código é executado novamente com a variável de tipo diferente, de modo que o código compilado existente não pode ser usado para que o intérprete salte - até que o código seja compilado novamente para o novo tipo ...
hmijail
Você está me citando algo que eu não disse, remova-o porque está totalmente errado. Microcódigo não tem nada a ver com o sistema operacional; é algo interno ao microprocessador. 32 bits ou 64 bits também não tem nada a ver com isso.
hmijail
3

Os tipos de dados não são um recurso de hardware. A CPU conhece alguns (bem, muitos) comandos diferentes. Esses são chamados de conjunto de instruções de uma CPU.

Um dos mais conhecidos é o conjunto de instruções x86 . Se você pesquisar "multiplicar" nesta página, obterá 50 resultados. MULPDe MULSDpara a multiplicação de duplas, FIMULpara multiplicação inteira, ...

Esses comandos funcionam em registradores. Registradores são slots de memória que podem conter um número fixo de bits (geralmente 32 ou 64, dependendo da arquitetura que sua CPU usa), independentemente do que esses bits representem. Portanto, a instrução CPU interpreta os valores dos registradores de uma maneira diferente, mas os próprios valores não têm tipos.

Um exemplo foi dado no PyCon 2017 por Stuart Williams :

insira a descrição da imagem aqui

Martin Thoma
fonte
1
Observe que isso não é estritamente verdadeiro: existem registros de finalidade especial que não podem conter valores arbitrários (por exemplo, registros de ponteiro que não são apenas qualquer endereço e não permitem adições arbitrárias ou registros de ponto flutuante onde você pode armazene valores não normalizados). Mas sua resposta está correta para registros de uso geral na maioria das arquiteturas.
Gilles 'SO- stop be evil'
2

... que um programa em particular diz à CPU para buscar as informações de um endereço específico e o programa define como tratá-las.

Exatamente. Mas a RAM não é lida "sequencialmente" e significa Memória de acesso aleatório, que é exatamente o oposto.

Além de saber o que um byte é , você nem sei se é um byte , ou um fragmento de um item de maior, como um número de ponto flutuante.

Eu gostaria de acrescentar outras respostas, dando alguns exemplos específicos.

Considere 01000001. O programa pode copiá-lo de um lugar para outro como parte de uma grande parcela de dados, sem levar em consideração seu significado. Mas copiar isso para o endereço usado pelo buffer de vídeo em modo de texto fará com que a letra Aseja exibida em alguma posição na tela. A mesma ação exata quando a placa está no modo de gráficos CGA exibirá um pixel vermelho e um pixel azul.

Em um registro, poderia ser o número 65 como um número inteiro. Fazer aritmética para definir o bit de 32 pode significar algo sem contexto, mas pode estar especificamente alterando uma letra para minúscula.

A CPU 8086 (ainda) possui instruções especiais chamadas DAA que são usadas quando o registro contém 2 dígitos decimais; portanto, se você acabou de usar essa instrução, está interpretando como dois dígitos 41.

Os programas falham porque uma palavra de memória é lida, pensando que é um ponteiro quando algo foi armazenado lá.

Usando um depurador, inspecionando a memória, um mapa é usado para orientar a interpretação para exibição. Sem essas informações de símbolo, um depurador de baixo nível permite especificar: mostrar este endereço como palavras de 16 bits, mostrar esse endereço como ponto flutuante longo, como seqüências de caracteres ... o que for. Olhando para um despejo de pacotes de rede ou um formato de arquivo desconhecido, intrigá-lo é um desafio.

Essa é uma fonte importante de poder e flexibilidade na arquitetura moderna dos computadores: uma célula de memória pode significar qualquer coisa , dados ou instrução, implícita apenas no que "significa" para o programa, pelo que faz com o valor e como isso afeta as operações subseqüentes. o significado é mais profundo que a largura do número inteiro: esses caracteres são ... caracteres em ascii ou ebcdic? Formando palavras em inglês ou códigos de produto SQU? O endereço para o qual enviar ou o endereço de retorno de onde veio? A interpretação nível mais baixo (bits lógicos; integer-like, assinado ou não assinado; flutuador; BCD; ponteiro) é contextual no nível de instrução de conjunto, mas você vê que é tudo o contexto em algum nível: o deendereço é o que é devido à localização impressa no envelope. É contextual às regras do carteiro, não da CPU. O contexto é um grande continuum, com bits em uma extremidade.


※ Nota de rodapé: a instrução DAA é codificada como um byte 00100111. Portanto, esse byte é a instrução acima mencionada, se lida no fluxo de instruções, e os dígitos, 27se interpretados como dígitos bcd, e 0x27 = 39 como um número inteiro, que é o número 9 em ASCII e parte da tabela de interrupção (metade da INT 13 Endereço de 2 bytes, usado para rotinas de serviço do BIOS).

JDługosz
fonte
1

A única maneira pela qual o computador sabe que um local de memória é uma instrução é que um registro para fins especiais chamado ponteiro de instruções os aponte para um ponto ou outro. Se o ponteiro da instrução apontar para uma palavra da memória, ele será carregado como uma instrução. Fora isso, o computador não tem como saber a diferença entre programas e outros tipos de dados.

Dummy Dum
fonte