Existem plataformas onde os pointers para diferentes tipos têm tamanhos diferentes?

O padrão C permite que pointers para tipos diferentes tenham tamanhos diferentes, por exemplo, sizeof(char*) != sizeof(int*) é permitido. No entanto, ele exige que, se um ponteiro for convertido em um void* e, em seguida, convertido de volta para seu tipo original, ele deve ser comparado como igual ao seu valor original. Portanto, segue logicamente que sizeof(void*) >= sizeof(T*) para todos os tipos T , correto?

Na maioria das plataformas comuns em uso hoje (x86, PPC, ARM, e variantes de 64 bits, etc.), o tamanho de todos os pointers é igual ao tamanho do registro nativo (4 ou 8 bytes), independentemente do tipo apontado. Existem plataformas esotéricas ou incorporadas nas quais os pointers para diferentes tipos podem ter tamanhos diferentes? Eu estou especificamente perguntando sobre pointers de dados , embora eu também esteja interessado em saber se existem plataformas onde os pointers de function têm tamanhos incomuns.

Definitivamente não estou perguntando sobre as funções ponteiro para membros do C ++ e ponteiro para membro. Eles assumem tamanhos incomuns em plataformas comuns e podem até variar dentro de uma plataforma, dependendo das propriedades da class ponteiro-para (inheritance não-polimórfica, inheritance única, inheritance múltipla, inheritance virtual ou incompleta).

Resposta do C FAQ :

A série Prime 50 usou o segmento 07777, compensou 0 para o ponteiro nulo, pelo menos para PL / I. Modelos posteriores usaram o segmento 0, offset 0 para pointers nulos em C, necessitando de novas instruções como TCNP (Test C Null Pointer), evidentemente como uma referência a todo o código C existente e mal escrito que fazia suposições incorretas. As máquinas Prime mais antigas, endereçadas à palavra, também eram notórias por exigir pointers de byte maiores (char * ‘s) do que pointers de palavras (int *’ s).

A série MV do Eclipse do Data General possui três formatos de pointers com suporte arquitetural (pointers de palavra, byte e bit), dois dos quais são usados ​​pelos compiladores C: pointers de bytes para char * e void * e pointers de palavras para todo o resto. Por razões históricas durante a evolução da linha MV de 32 bits a partir da linha Nova de 16 bits, os pointers de palavra e os pointers de byte tinham os bits de proteção de deslocamento, indireto e de toque em diferentes locais da palavra. Passar um formato de ponteiro incompatível para uma function resultou em falhas de proteção. Eventualmente, o compilador MV C adicionou muitas opções de compatibilidade para tentar lidar com o código que tinha erros de incompatibilidade de tipo de ponteiro.

Alguns mainframes da Honeywell-Bull usam o padrão de bits 06000 para pointers nulos (internos).

O CDC Cyber ​​180 Series possui pointers de 48 bits que consistem em um anel, segmento e deslocamento. A maioria dos usuários (no anel 11) tem pointers nulos de 0xB00000000000. Era comum em máquinas de complemento de CDC antigas usar uma palavra de todos os bits como sinalizador especial para todos os tipos de dados, incluindo endereços inválidos.

A antiga série HP 3000 usa um esquema de endereçamento diferente para endereços de bytes do que para endereços de palavras; Como várias das máquinas acima, portanto, usa representações diferentes para pointers char * e void * do que para outros pointers.

O Symbolics Lisp Machine, uma arquitetura marcada, não tem sequer pointers numéricos convencionais; ele usa o par (basicamente um identificador inexistente) como um ponteiro nulo C.

Dependendo do “ modelo de memory ” em uso, os processadores da família 8086 (compatíveis com PC) podem usar pointers de dados de 16 bits e pointers de function de 32 bits, ou vice-versa.

Algumas máquinas Cray de 64 bits representam int * nos 48 bits inferiores de uma palavra; char * usa adicionalmente alguns dos 16 bits superiores para indicar um endereço de bytes dentro de uma palavra.

Links adicionais: Uma mensagem de Chris Torek com mais detalhes sobre algumas dessas máquinas.

Não exatamente o que você está perguntando, mas nos dias DOS / Windows de 16 bits, você tinha a distinção entre um ponteiro e um ponteiro distante, sendo este último de 32 bits.

Eu posso ter a syntax errada …

 int *pInt = malloc(sizeof(int)); int far *fpInt = _fmalloc(sizeof(int)); printf("pInt: %d, fpInt: %d\n", sizeof(pInt), sizeof(fpInt)); 

Saída:

pInt: 2, fpInt 4

Portanto, segue logicamente que sizeof(void*) >= sizeof(T*) para todos os tipos T, correto?

Isso não segue necessariamente, já que sizeof é sobre a representação de armazenamento, e nem todos os padrões de bits precisam ser valores válidos. Eu acho que você poderia escrever uma implementação em conformidade onde sizeof(int*) == 8 , sizeof(void*) == 4 , mas não há mais que 2 ^ 32 valores possíveis para um int *. Não tenho certeza por que você iria querer.

Nos anos dourados do DOS, 8088s e memory segmentada, era comum especificar um “modelo de memory” no qual, por exemplo, todo o código caberia em 64k (um segmento), mas os dados poderiam abranger vários segmentos; Isso significava que um ponteiro de function seria 2 bytes, um ponteiro de dados, 4 bytes. Não tenho certeza se alguém ainda está programando para máquinas desse tipo, talvez alguns ainda sobrevivam em usos incorporados.

Pode-se facilmente imaginar uma máquina de arquitetura de Harvard com tamanhos diferentes para pointers de function e todos os outros pointers. Não sei de um exemplo …

Ponteiros próximos e distantes ainda são usados ​​em alguns microcontroladores incorporados com flash ou RAM paginados, para permitir que você aponte para dados na mesma página (ponteiro próximo) ou outra página (ponteiro distante, que é maior porque inclui informações de página).

Por exemplo, o microcontrolador HCS12 da Freescale usa uma arquitetura Von Neumann de 16 bits, o que significa que nenhum endereço pode ter mais de 16 bits. Devido à limitação que isso colocaria na quantidade de espaço de código disponível, há um registro de página de 8 bits.

Então, para apontar para os dados na mesma página de código, basta especificar o endereço de 16 bits; este é um ponteiro próximo.

Para apontar para dados em outra página de código, você deve include o número de página de 8 bits e o endereço de 16 bits nessa página, resultando em um ponteiro distante de 24 bits.

É possível que o tamanho de pointers para dados seja diferente de pointers para funções, por exemplo. É comum que isso ocorra em um microprocessador para sistemas embarcados. As máquinas de arquitetura de Harvard, como a dmckee mencionada, tornam isso fácil de acontecer.

Acontece que isso faz com que o gcc seja um problema para se desenvolver! 🙂

Edit: Eu não posso ir para os detalhes da máquina específica que estou falando, mas deixe-me acrescentar por que máquinas de Harvard facilitam isso acontecer. A arquitetura de Harvard tem armazenamento e caminhos diferentes para instruções e dados, portanto, se o barramento das instruções for “maior” que o dos dados, você terá um ponteiro de function cujo tamanho é maior que um ponteiro para os dados!