Recentemente, tenho tentado explicar ponteiros de maneira visual, como cartões de memória flash.
Pergunta 001: Este é o desenho de um local na memória do computador. É verdade que seu endereço é
0x23452
? Por quê?Resposta: Sim, porque
0x23452
descreve onde o computador pode encontrar esse local.
Pergunta 002: É verdade que o caractere
b
está armazenado dentro do local da memória0x23452
? Por quê?Resposta: Não, porque o personagem
a
está realmente armazenado dentro dele.
Pergunta 003: É verdade que um ponteiro está armazenado dentro do local da memória
0x23452
? Por quê?Resposta: Sim, porque o endereço da localização da memória
0x34501
é armazenado dentro dela.
Pergunta 004: É verdade que um ponteiro está armazenado dentro do local da memória
0x23452
? Por quê?Resposta: Sim, porque o endereço de outro local de memória está armazenado dentro dele.
Agora, a parte que me preocupou. Um engenheiro de software explicou os ponteiros para mim assim:
Um ponteiro é uma variável cujo valor é o endereço de memória de outra variável.
Com base nos quatro flashcards que mostrei a todos, eu definiria ponteiros de uma maneira ligeiramente diferente:
Um ponteiro é um local de memória cujo valor é o endereço de memória de outro local de memória.
É seguro dizer que uma variável é a mesma coisa que um local de memória?
Se não, então quem está certo? Qual é a diferença entre uma variável e um local de memória?
fonte
a
,0x23453
.nil
etc coisas dentro deles são os valores. Isso pode lhe parecer óbvio, mas eu não me sentiria confortável em dar respostas decisivas a essas perguntas sem ver como esses campos são definidos. Realmente não há como saber se,a
na segunda imagem, há um caractere, uma string (se houver alguma diferença) ou o nome de uma variável. Se é uma string,nil
também é uma string? Ou um valor "nulo"?Respostas:
Uma variável é uma construção lógica que é direcionada à intenção de um algoritmo, enquanto um local de memória é uma construção física que descreve a operação de um computador. De um modo geral, para executar um programa, existe um mapeamento (gerado pelo compilador) entre a noção lógica de uma variável e o armazenamento do computador.
(Mesmo na linguagem assembly, temos uma noção de variáveis (lógicas) que vão para o algoritmo e a intenção e os locais (físicos) da memória, embora sejam mais conflitantes no assembly.)
Uma variável é um conceito de nível mais alto. Uma variável representa um desconhecido (como em matemática ou atribuição de programação) ou um marcador de posição que pode ser substituído por um valor (como em programação: parâmetros).
Um local de memória é um conceito de nível mais baixo. Um local de memória pode ser usado para armazenar um valor, às vezes, para armazenar o valor de uma variável. No entanto, um registro de CPU é outra maneira de armazenar o valor de algumas variáveis. Os registradores da CPU também são locais de armazenamento de baixo nível, mas não são locais de memória, pois não possuem endereços, apenas nomes.
Em certo sentido, uma variável é um mecanismo de abstração para expressar a intenção do programa, enquanto um local de memória é uma entidade física do ambiente de processamento que fornece armazenamento e recuperação.
Não podemos dizer com certeza. Só porque existe um valor lá que funcionaria como endereço, não significa que seja esse endereço, mas sim o número inteiro (decimal) 144466. Não podemos fazer suposições sobre a interpretação dos valores apenas com base em como eles aparecem numericamente.
Esta é realmente uma pergunta estranha. Eles esperam algumas suposições com base nas caixas, no entanto, observe que os endereços aumentam em 1 para cada caixa. Em qualquer computador moderno, isso significa que cada caixa pode conter uma capacidade de endereçamento de byte a byte já é a norma há décadas. No entanto, um byte tem apenas 8 bits e pode variar de 0 a 255 (para valores não assinados); no entanto, eles mostram um valor muito maior armazenado em um desses endereços, muito suspeito. (Isso poderia funcionar se fosse uma máquina endereçada por palavras, mas não diz isso, e poucas máquinas atualmente o são, embora algumas máquinas educacionais sejam).
Embora existam situações em que esse pensamento esteja correto, você está misturando metáforas aqui. A noção de uma variável vai para o algoritmo e sua intenção - não é necessário assumir que todas as variáveis têm localizações de memória. Algumas variáveis (especialmente matrizes) possuem locais de memória porque os locais de memória suportam endereçamento (enquanto os registros da CPU podem ser nomeados apenas como não indexados).
Para execução, existe um mapeamento lógico entre variáveis e instruções e localizações da memória do processador e seqüências de instruções do processador. Uma variável cujo valor nunca muda (por exemplo, uma constante) nem exige necessariamente um local de memória, pois o valor pode ser reproduzido à vontade (por exemplo, conforme necessário para seqüências de código geradas pelo compilador).
fonte
for
índice de loop quando o compilador decide desenrolar completamente o loop. Em nenhum lugar do código de saída produzido (seja montagem, código de máquina ou código de bytes), há um local de memória no qual o contador de loop é armazenado. Mas ainda é uma variável.source
para uma matriz de igual comprimento,dest
um loop codificadofor (int i=0; i<8; ++i) dest[i] = source[i];
pode ser compilado até algo equivalente à repetição dodest++ = source++;
número apropriado de vezes. Com o próprio contador de loops não está em evidência em lugar algum (nem mesmo em registro), e apenas o número de repetições informa sobre a condição do loop.Não. A variável e a localização da memória são duas abstrações em dois níveis diferentes de abstração. Variável e ponteiros são um conceito de nível superior no nível do código / idioma, a localização da memória é um conceito de nível inferior no nível da máquina. Depois que um código foi compilado em um executável, não há mais variáveis. Tentar falar sobre a localização da memória e variáveis dessa maneira é um erro categórico.
Uma variável pode ser implementada usando a memória, mas nem sempre como um compilador pode otimizar um cálculo e fazer todos os cálculos relacionados a uma variável inteiramente em registradores, ou pode colocar uma única variável em vários locais da memória ou pode usar uma única memória local para várias variáveis.
Essa série de cartões de memória é tão confusa que eles não apenas não estão certos, mas também não estão errados.
fonte
Once a code had been compiled into an executable, there's no longer any variables.
Isso é algo que eu discordo. É correto que sua variável como você a conhece (ou seja, com esse nome) não exista mais, mas seu fraseado parece sugerir que o executável compilado usa apenas endereços de memória. Isso não está correto. Seu executável compilado, mas não em execução, não tem idéia de quais endereços de memória serão usados quando executados. O conceito de uma variável (isto é, uma referência reutilizável para qualquer endereço de memória que será atribuído em tempo de execução) ainda existe dentro do executável compilado.Variáveis são construções de linguagem . Eles têm um nome, residem em um escopo, podem ser referenciados por outras partes do código, etc. Eles são uma entidade lógica . O compilador é livre para implementar essa construção de linguagem da maneira que desejar, desde que o comportamento observável seja o prescrito pelo padrão de linguagem. Como tal, a variável nem precisa ser armazenada em nenhum lugar se o compilador puder provar que isso não é necessário.
Locais de memória são um conceito de hardware . Eles significam um lugar na memória virtual / física. Cada local de memória possui exatamente um endereço físico e qualquer quantidade de endereços virtuais que podem ser usados para manipulá-lo. Mas sempre há exatamente um byte armazenado em cada local da memória.
Ponteiros são um tipo especial de valores . Dizer que algo é um ponteiro é semelhante a dizer que algo é do tipo
double
. Significa quantos bits são usados para o valor e como esses bits são interpretados, mas isso não significa que esse valor seja armazenado em uma variável, nem significa que esse valor seja armazenado na memória.Para dar um exemplo em C: Quando tenho uma matriz 2D
int foo[6][7];
e acesso um elemento delafoo[1][2]
,foo
é uma variável que mantém uma matriz. Quandofoo
é usado neste contexto, é transformado em um ponteiro para o primeiro elemento da matriz. Esse ponteiro não é armazenado em nenhuma variável, nem na memória, seu valor é gerado apenas dentro de um registro da CPU, usado e depois esquecido. Da mesma forma, a expressãofoo[1]
é transformada em outro ponteiro nesse contexto, que, novamente, não está em uma variável, não está armazenado na memória, mas é computado na CPU, usado e esquecido. A variável de três conceitos , localização da memória e ponteiro são realmente três conceitos diferentes.Aliás, eu realmente quis dizer "sempre há exatamente um byte armazenado em cada local da memória". Esse não foi o caso na era da pedra da computação, há cinquenta anos atrás, mas é verdade para praticamente todo o hardware que está sendo usado atualmente. Sempre que você armazena um valor na memória maior que um byte, na verdade você está usando vários locais de memória consecutivos. Ou seja (assumindo a ordem dos bytes big endian), o número 0x01234567 é armazenado na memória como
(Pequenas máquinas endian, como a arquitetura X86, armazenam os bytes na ordem inversa.) Isso também vale para ponteiros: um ponteiro em uma máquina de 64 bits é armazenado em oito bytes consecutivos, cada um com seu próprio endereço de memória. Você não pode olhar para uma célula de memória e dizer: "Oh, isso é um ponteiro!" Você sempre vê bytes apenas quando olha para a memória .
fonte
Deixe-me focar na sua pergunta real - "quem está certo?" ao comparar essas duas instruções:
A resposta para isso é nenhuma . O primeiro fala de um "endereço de memória de outra variável", mas as variáveis não têm necessariamente endereços de memória, como as outras respostas já explicadas. O segundo diz "um ponteiro é um local de memória", mas um ponteiro é literalmente apenas um número, que pode ser armazenado em uma variável, mas, como antes, uma variável não necessariamente tem um endereço de memória.
Alguns exemplos para declarações mais precisas:
"Um ponteiro é um número que representa o endereço de memória de um local de memória" ou
"Uma variável de ponteiro é uma variável cujo valor é o endereço de memória de um local de memória."
"Um endereço de memória pode conter um ponteiro representando o endereço de memória de um local de memória."
Observe que algumas vezes o termo "ponteiro" é usado como um atalho para "variável do ponteiro", o que é aceitável desde que não leve à confusão.
fonte
Eu certamente não diria que um ponteiro é um local de memória que contém um endereço. Por um lado, não conheço uma arquitetura em que
0x23453
possa caber em um único byte. :) Mesmo se você ignorar a distinção byte / palavra, ainda terá o problema de que todo local de memória contenha um endereço. Endereços são apenas números, e o conteúdo da memória são apenas números.Eu acho que o truque aqui é que "ponteiro" descreve a intenção humana , não qualquer característica específica da arquitetura. É semelhante a como um "caractere" ou "string" não é algo concreto que você pode ver na memória - esses também são apenas números, mas funcionam como strings, porque é assim que são tratados. "Ponteiro" significa apenas um valor destinado a ser usado como um endereço.
Honestamente, se seu objetivo é ensinar um idioma específico (objetivo C?), Não tenho certeza de que desenhar a fita de memória clássica seja útil. Você já está mentindo para branco, mostrando valores digitados e valores muito grandes para um byte. Ensine semântica, não mecânica - o principal insight sobre ponteiros é que eles fornecem indireção , que é uma ferramenta extremamente útil para entender.
Eu acho que uma boa comparação pode ser com um URL, que informa onde encontrar alguns dados, mas não os dados em si. Me ouça:
Você raramente se importa com o que realmente é o URL ; a grande maioria deles é esquivada em links com nomes. Muitas pessoas usam a internet sem saber exatamente como um URL resulta em uma página; algumas pessoas ignoram completamente os URLs.
Nem toda string é um URL ou deve ser usada como um URL.
Se você tentar visitar um URL falso ou uma página que existia, mas que foi excluída desde então, você recebe um erro.
Um URL pode apontar para uma imagem, algum texto, música ou qualquer número de outros itens individuais - ou pode apontar para uma página com uma variedade de itens contidos. É muito comum ter várias páginas com layouts semelhantes, mas com dados diferentes.
Se você cria uma página da Web e deseja consultar dados em alguma outra página da Web, não precisa copiar e colar tudo; você pode simplesmente fazer um link para ele.
Qualquer número de outras páginas pode ser vinculado ao mesmo URL.
Se você tiver uma coleção de páginas semelhantes, poderá criar uma página de índice que lista os links para todas elas ou apenas um link "próximo" na parte inferior da página 1, que leva à página 2 e assim por diante. As vantagens e desvantagens de ambas as abordagens são imediatamente óbvias, especialmente se você considerar o que o webmaster precisaria fazer para adicionar ou remover páginas em vários lugares.
Esta analogia deixa muito claro o que os ponteiros são para , o que é fundamental para compreendê-los - caso contrário, eles parecem apenas arbitrárias, complicado, e sem sentido. Compreender como algo funciona é muito mais fácil se você já entende o que faz e por que é útil. Se você já internalizou que um ponteiro é uma caixa preta que indica onde está outra coisa, e então você aprende sobre os meandros do modelo de memória, a representação de ponteiros como endereços é bastante óbvia. Além disso, o ensino de semântica colocará seus alunos em um lugar muito melhor para entender e inventar outras formas de indireção - o que é bom quando a maioria dos idiomas principais não tem ponteiros!
fonte
every memory location contains an address
- Todo local de memória tem um endereço. Não está contido em nenhum lugar, exceto talvez em uma variável de ponteiro.Sei que você já aceitou uma resposta, e essa pergunta já tem cinco respostas, mas há um ponto que elas não mencionam, que eu acho que te enganou. Os livros didáticos de CS geralmente tentam ser agnósticos quanto à escolha da linguagem de programação, o que leva à suposição implícita de que a terminologia usada para descrever as coisas é universal. Não é.
Em C, o operador e comercial unário é chamado de operador "endereço de". Os programadores C não hesitam em dizer que a expressão é
&x
avaliada para o endereço da variável x. É claro que eles significam "o endereço de memória no qual o valor da variável x é armazenado", mas ninguém é tão pedante em conversas casuais. Em C, a palavra "ponteiro" geralmente se refere ao tipo de dado de uma variável que possui um endereço de memória como valor. Ou equivalente ao tipo de dados do valor. Mas algumas pessoas usariam "ponteiro" como o próprio valor.Em Java, todas as variáveis do tipo de objeto ou matriz se comportam muito como ponteiros C (exceto para a aritmética dos ponteiros), mas os programadores Java as chamam de referências, não de ponteiros.
O C ++ considera referências e ponteiros como conceitos diferentes. Eles estão relacionados, mas não são exatamente a mesma coisa; portanto, os programadores de C ++ precisam fazer a distinção na conversa. O "e comercial" é lido como "endereço de" em alguns contextos e "referência para" em outros.
É assim que um programador C pode descrevê-lo, usando "um ponteiro" no mesmo sentido que "um int". (Como em "um ponteiro mantém um endereço de memória enquanto um int mantém um número inteiro dentro de um determinado intervalo").
É uma maneira estranha de dizê-lo, porque requer uma definição muito vaga e informal de "é".
Seria mais claro dizer que um endereço de memória é o local na memória em que o valor de uma variável é armazenado. (Concedido, nem todas as variáveis são armazenadas na memória, devido a otimizações do compilador, mas qualquer variável cujo endereço seja utilizado
&x
será).fonte
A instrução Um ponteiro é uma variável cujo valor é o endereço de memória de outra variável é simplificada demais. Porém, quando o leitor entender exatamente o que é um local de memória e como ele difere de uma variável, ele já entenderá o que é exatamente um ponteiro e, portanto, não precisará mais confiar nessa explicação imprecisa.
A instrução Um ponteiro é um local de memória cujo valor é o endereço de memória de outro local de memória está incorreto. O valor de um ponteiro não precisa ser armazenado em um local de memória e é discutível se um ponteiro precisar apontar para um local de memória, dependendo da definição pretendida de "memória".
Um local de memória é um dos vários locais possíveis onde os dados podem ser armazenados. Esses dados podem ser uma variável ou parte de uma variável. Variáveis são uma maneira de rotular dados.
fonte
Esta resposta se concentra em C e C ++; isso parece apropriado, pois sua pergunta se refere a ponteiros que são parte mais integrante do C / C ++ do que de outros idiomas. A maior parte deste post será aplicada à maioria das linguagens compiladas sem um tempo de execução elaborado (como Pascal ou Ada, mas não como Java ou C #).
As boas respostas já dadas enfatizam que uma variável é uma construção da linguagem em um nível mais abstrato do que a memória física. Eu gostaria de enfatizar, no entanto, que essa abstração tem uma certa lógica e sistema:
A abstração consiste principalmente em usar um nome em vez de um endereço literal.
A idéia principal é que uma variável seja um identificador nomeado para um objeto digitado; objetos em C / C ++ geralmente estão na memória. Os idiomas adicionam alguns detalhes sobre gerenciamento de vida útil e empacotamento de dados para conversões de tipo. O conceito de variáveis é mais abstrato do que endereços físicos, porque na verdade não nos importamos com o valor numérico dos endereços ou com a localização exata das funções na memória. Nós simplesmente os nomeamos e depois os nomeamos, e o compilador, o vinculador e o sistema de tempo de execução cuidam dos detalhes.
E não finja que C / C ++ seja independente de memória: afinal, existe o operador de endereço universalmente aplicável. Sim, é verdade, você não pode usar o endereço de uma variável C na classe de armazenamento de registro; mas quando você usou um pela última vez? É uma exceção especial ao conceito geral, não uma rejeição total do argumento. A regra geral é, ao contrário, que pegar um endereço de uma variável realmente força o compilador a criar um objeto na memória, mesmo que não o faça de outra forma (por exemplo, com constantes). O conceito "identificador com nome" também é um bom paradigma para referências em C ++: Uma referência é apenas outro nome para o mesmo objeto.
Quando escrevi o assembler em linha para 68k, foi bom ver como você poderia usar nomes de variáveis como compensações para endereçar registros (e você poderia usar os nomes de variáveis declaradas em
register
vez dos nomes de registro bare metal!). Para o compilador, uma variável é um deslocamento de endereço constante. Para reiterar: As variáveis são identificadas como identificadores, geralmente para objetos na memória.fonte
Parece que a pergunta é voltada para uma linguagem popular formada pelo aprimoramento do Padrão C com a garantia adicional "Nos casos em que algumas partes do Padrão ou da documentação de uma implementação descrevem o comportamento de alguma ação, e outra parte o categoriza como indefinido , a parte anterior domina. ", bem como uma definição de" variável "consistente com o uso do termo por outras línguas.
Nesse idioma, cada local de memória pode ser visualizado como uma caixa de correio numerada que sempre mantém um número (normalmente oito) de bits, cada um dos quais pode ser independentemente zero ou um. Os locais da memória geralmente são organizados em linhas de dois, quatro ou oito. e algumas operações são processadas em vários locais consecutivos de memória ao mesmo tempo. Dependendo da máquina, algumas operações que operam em grupos de dois, quatro ou oito locais de memória podem ser limitadas a operar em locais dentro de uma única linha. Além disso, enquanto algumas máquinas podem ter uma única sala de caixas de correio numeradas consecutivamente, outras podem ter vários grupos separados de caixas de correio numeradas.
Uma variável identifica um intervalo de locais de memória associados exclusivamente a ele e um tipo como o qual esses locais de memória devem ser interpretados. A leitura de uma variável fará com que os bits em seus locais de armazenamento associados sejam interpretados de maneira apropriada ao tipo da variável, e a gravação de uma variável fará com que os bits associados sejam configurados de maneira apropriada ao seu tipo e valor.
Um endereço encapsula todas as informações necessárias para identificar uma caixa de correio. Isso pode ser armazenado como um número simples ou como algum tipo de designador de grupo, juntamente com o número de uma caixa de correio dentro desse grupo.
A aplicação do
&
operador a uma variável produzirá um ponteiro que encapsula o endereço e o tipo do mesmo. A aplicação do unário*
ou do[]
operador a um ponteiro fará com que os bits das caixas de correio iniciando no endereço encapsulado sejam interpretados ou configurados de maneira apropriada ao tipo encapsulado.fonte
Estou chegando atrasado para esta festa, mas não resisto a colocar meus 2 centavos.
Nesses momentos, qual é a diferença entre os valores armazenados nesses locais de memória?
Tempo 1
Tempo 2
Resposta correta: nada. Todos são valores idênticos sendo apresentados com diferentes interpretações de seu significado.
Como eu sei disso? Porque sou eu quem inventou isso. Você ainda não sabe disso.
Você está encontrando algo que eu chamo de problema fora da banda . Como interpretar corretamente o significado desses valores não é armazenado aqui. Esse conhecimento é armazenado em outro lugar. No entanto, quando você apresenta esses valores no papel, coloca essa interpretação. Isso significa que você adicionou informações que simplesmente não existem nesses locais de memória.
Por exemplo, os valores aqui são idênticos, mas você só sabe que isso é verdade se você estiver correto ao assumir uma codificação de caracteres ASCII / UTF-8. Foi assim que obtive o primeiro, em vez de EBCDIC . E você também deve assumir que o segundo é expressões hexadecimais dos valores numéricos armazenados nesses locais de memória, que podem ser ponteiros para outros endereços, em vez de dizer referências a seqüências de caracteres que começam com "0x". : P
Nada armazenado nesses locais de memória informa que qualquer uma dessas suposições está correta. Essa informação pode ser armazenada. Mas seria armazenado em outro lugar.
Este é o problema de apresentação . Você não pode expressar nenhum número sem antes concordar em como apresentá-lo. Você pode se basear em suposições, convenções e contexto, mas se você a analisar profundamente, quando a apresentação não for definida explicitamente, a única resposta verdadeiramente correta será "informações insuficientes".
fonte