SUMÁRIO BREVE
(que também colocarei no topo):
(0) Pensar em ponteiros como endereços geralmente é uma boa ferramenta de aprendizado e, muitas vezes, é a implementação real de ponteiros para tipos de dados comuns.
(1) Mas em muitos, talvez na maioria, os ponteiros de compiladores para funções não são endereços, mas são maiores que um endereço (normalmente 2x, às vezes mais), ou são realmente ponteiros para uma estrutura na memória que contém os endereços de funções e coisas como uma piscina constante.
(2) Ponteiros para membros de dados e ponteiros para métodos geralmente são ainda mais estranhos.
(3) Código x86 legado com problemas de ponteiro FAR e NEAR
(4) Vários exemplos, principalmente o IBM AS / 400, com "indicadores de gordura" seguros.
Tenho certeza que você pode encontrar mais.
DETALHE:
UMMPPHHH !!!!! Até agora, muitas das respostas são respostas típicas de "programador weenie" - mas não de compilador ou hardware. Desde que eu finjo ser um weenie de hardware, e muitas vezes trabalho com weenies de compilador, deixe-me colocar meus dois centavos:
Em muitos, provavelmente na maioria dos compiladores C, um ponteiro para dados do tipo T
é, de fato, o endereço de T
.
Bem.
Mas, mesmo em muitos desses compiladores, certos ponteiros NÃO são endereços. Você pode dizer isso olhando para sizeof(ThePointer)
.
Por exemplo, ponteiros para funções às vezes são bem maiores que endereços comuns. Ou, eles podem envolver um nível de indireção. Este artigofornece uma descrição, envolvendo o processador Intel Itanium, mas já vi outras. Normalmente, para chamar uma função, você deve conhecer não apenas o endereço do código da função, mas também o endereço do pool constante da função - uma região da memória na qual as constantes são carregadas com uma única instrução de carregamento, em vez de o compilador ter que gerar uma constante de 64 bits em várias instruções Load Immediate, Shift e OR. Portanto, em vez de um único endereço de 64 bits, você precisa de 2 endereços de 64 bits. Algumas ABIs (Application Binary Interfaces) movem isso em 128 bits, enquanto outras usam um nível de indireção, com o ponteiro de função sendo o endereço de um descritor de função que contém os 2 endereços reais mencionados. Qual é melhor? Depende do seu ponto de vista: desempenho, tamanho do código, e alguns problemas de compatibilidade - geralmente o código pressupõe que um ponteiro possa ser convertido em um comprimento longo ou muito longo, mas também pode assumir que o comprimento longo é exatamente de 64 bits. Esse código pode não estar em conformidade com os padrões, mas, no entanto, os clientes podem querer que ele funcione.
Muitos de nós têm lembranças dolorosas da antiga arquitetura segmentada Intel x86, com NEAR POINTERs e FAR POINTERS. Felizmente, eles já estão quase extintos, então apenas um resumo rápido: no modo real de 16 bits, o endereço linear real era
LinearAddress = SegmentRegister[SegNum].base << 4 + Offset
Enquanto no modo protegido, pode ser
LinearAddress = SegmentRegister[SegNum].base + offset
com o endereço resultante sendo verificado em relação a um limite definido no segmento. Alguns programas não usaram declarações de ponteiro C / C ++ FAR e NEAR realmente padrão, mas muitos acabaram de dizer *T
--- mas havia opções de compilador e vinculador, por exemplo, ponteiros de código podem estar próximos a ponteiros, apenas um deslocamento de 32 bits em relação ao que estiver em o registro CS (Segmento de código), enquanto os ponteiros de dados podem ser ponteiros FAR, especificando um número de segmento de 16 bits e um deslocamento de 32 bits para um valor de 48 bits. Agora, essas duas quantidades certamente estão relacionadas ao endereço, mas como não são do mesmo tamanho, qual é o endereço? Além disso, os segmentos também possuíam permissões - somente leitura, leitura e gravação, executável - além de itens relacionados ao endereço real.
Um exemplo mais interessante, IMHO, é (ou talvez fosse) a família IBM AS / 400. Este computador foi um dos primeiros a implementar um sistema operacional em C ++. Os ponteiros dessa máquina geralmente eram 2X o tamanho real do endereço - por exemplo, esta apresentaçãodiz, ponteiros de 128 bits, mas os endereços reais eram de 48 a 64 bits e, novamente, algumas informações extras, o que é chamado de recurso, que forneciam permissões como leitura, gravação e um limite para impedir o estouro do buffer. Sim: você pode fazer isso de forma compatível com C / C ++ - e, se isso fosse onipresente, o PLA chinês e a máfia eslava não invadiriam tantos sistemas de computadores ocidentais. Porém, historicamente, a maioria dos programas em C / C ++ negligenciou a segurança do desempenho. O mais interessante é que a família AS400 permitiu que o sistema operacional criasse indicadores seguros, que poderiam ser atribuídos a códigos não privilegiados, mas que o código não privilegiado não poderia forjar ou adulterar. Novamente, a segurança e, embora seja compatível com os padrões, o código C / C ++ muito desleixado e não compatível com os padrões não funcionará em um sistema tão seguro. Mais uma vez, existem padrões oficiais,
Agora, vou sair da minha caixa de sabão de segurança e mencionar algumas outras maneiras pelas quais ponteiros (de vários tipos) geralmente não são realmente endereços: ponteiros para membros de dados, ponteiros para métodos de funções de membro e suas versões estáticas são maiores que um endereço comum. Como este post diz:
Existem muitas maneiras de resolver isso [problemas relacionados à herança única versus múltipla e herança virtual]. Veja como o compilador do Visual Studio decide manipulá-lo: Um ponteiro para uma função de membro de uma classe herdada de multiplicação é realmente uma estrutura ". E eles continuam dizendo" Converter um ponteiro de função pode alterar seu tamanho! ".
Como você provavelmente pode adivinhar pela minha identificação em (in) segurança, estive envolvido em projetos de hardware / software C / C ++ em que um ponteiro era tratado mais como uma capacidade do que como um endereço bruto.
Eu poderia continuar, mas espero que você entenda.
BREVE RESUMO
(que também colocarei no topo):
(0) pensar em ponteiros como endereços geralmente é uma boa ferramenta de aprendizado e geralmente é a implementação real de ponteiros para tipos de dados comuns.
(1) Mas em muitos, talvez na maioria, os ponteiros de compiladores para funções não são endereços, mas são maiores que um endereço (normalmente 2X, às vezes mais) ou são realmente ponteiros para uma estrutura na memória que contém os endereços de funções e coisas como uma piscina constante.
(2) Ponteiros para membros de dados e ponteiros para métodos geralmente são ainda mais estranhos.
(3) Código x86 legado com problemas de ponteiro FAR e NEAR
(4) Vários exemplos, principalmente o IBM AS / 400, com "indicadores de gordura" seguros.
Tenho certeza que você pode encontrar mais.