O padrão C garante que esse size_t
é um tipo que pode conter qualquer índice de matriz. Isso significa que, logicamente, size_t
deve ser capaz de manter qualquer tipo de ponteiro. Li em alguns sites que achei no Google que isso é legal e / ou sempre deve funcionar:
void *v = malloc(10);
size_t s = (size_t) v;
Então, em C99, o padrão introduziu os tipos intptr_t
e uintptr_t
, que são assinados e não assinados, com a garantia de poder conter ponteiros:
uintptr_t p = (size_t) v;
Então, qual é a diferença entre usar size_t
e uintptr_t
? Ambos não são assinados e devem poder manter qualquer tipo de ponteiro, para que pareçam funcionalmente idênticos. Existe alguma razão real e convincente para usar uintptr_t
(ou melhor ainda, a void *
) ao invés de a size_t
, além da clareza? Em uma estrutura opaca, onde o campo será tratado apenas por funções internas, existe alguma razão para não fazer isso?
Da mesma forma, ptrdiff_t
foi um tipo assinado capaz de conter diferenças de ponteiro e, portanto, capaz de conter quase todo o ponteiro, então como é diferente intptr_t
?
Todos esses tipos não servem basicamente versões trivialmente diferentes da mesma função? Se não, por que? O que não posso fazer com um deles que não posso fazer com outro? Se sim, por que o C99 adicionou dois tipos essencialmente supérfluos ao idioma?
Estou disposto a ignorar os ponteiros de função, pois eles não se aplicam ao problema atual, mas fique à vontade para mencioná-los, pois tenho uma suspeita de que eles serão essenciais para a resposta "correta".
size_t
e,uintptr_t
masptrdiff_t
e quanto aintptr_t
- eles não seriam capazes de armazenar o mesmo intervalo de valores em quase qualquer plataforma? Por que os tipos de números inteiros do tamanho de um ponteiro assinados e não assinados, principalmente septrdiff_t
já servem ao propósito de um tipo inteiro do tamanho de um ponteiro assinadosize_t
pelo menos 16 bits, masptrdiff_t
pelo menos 17 bits (o que na prática significa que provavelmente haverá pelo menos 32 bits).SIZE_MAX
que não deve ser 2 ** 64. Isso é usar endereçamento simples, lembre-se; nenhuma segmentação é necessária para haver uma incompatibilidade entreSIZE_MAX
e o intervalo de um ponteiro de dados.Em relação à sua declaração:
Na verdade, isso é uma falácia (um equívoco resultante de raciocínio incorreto) (a) . Você pode pensar que o último segue do primeiro, mas esse não é realmente o caso.
Ponteiros e índices de matriz não são a mesma coisa. É bastante plausível prever uma implementação em conformidade que limita as matrizes a 65536 elementos, mas permite que os ponteiros endereçam qualquer valor em um espaço de endereçamento massivo de 128 bits.
C99 afirma que o limite superior de uma
size_t
variável é definido porSIZE_MAX
e pode ser tão baixo quanto 65535 (consulte C99 TR3, 7.18.3, inalterado em C11). Os ponteiros seriam bastante limitados se estivessem restritos a essa faixa nos sistemas modernos.Na prática, você provavelmente descobrirá que sua suposição é válida, mas não é porque o padrão a garanta. Porque na verdade não garante isso.
(a) A propósito, essa não é uma forma de ataque pessoal, apenas afirmando por que suas declarações são errôneas no contexto do pensamento crítico. Por exemplo, o seguinte raciocínio também é inválido:
A fofura ou não dos filhotes não tem influência aqui, tudo o que afirmo é que os dois fatos não levam à conclusão, porque as duas primeiras frases permitem a existência de coisas fofas que não são filhotes.
Isso é semelhante à sua primeira declaração, não necessariamente obrigando a segunda.
fonte
ptrdiff_t
vs.intptr_t
).Vou deixar que todas as outras respostas sejam válidas em relação ao raciocínio com limitações de segmento, arquiteturas exóticas e assim por diante.
A simples diferença de nomes não é motivo suficiente para usar o tipo apropriado para a coisa correta?
Se você estiver armazenando um tamanho, use
size_t
. Se você estiver armazenando um ponteiro, useintptr_t
. Uma pessoa que estiver lendo seu código saberá instantaneamente que "aha, esse é o tamanho de algo, provavelmente em bytes" e "oh, aqui está um valor de ponteiro sendo armazenado como um número inteiro, por algum motivo".Caso contrário, você poderia apenas usar
unsigned long
(ou, nestes tempos modernos aquiunsigned long long
) para tudo. Tamanho não é tudo, nomes de tipo têm significado útil, pois ajuda a descrever o programa.fonte
size_t
campo.void*
,intptr_t
euintptr_t
são garantidos para ser capaz de representar qualquer ponteiro para dados.É possível que o tamanho da maior matriz seja menor que um ponteiro. Pense em arquiteturas segmentadas - os ponteiros podem ter 32 bits, mas um único segmento pode ser capaz de endereçar apenas 64 KB (por exemplo, a antiga arquitetura 8086 em modo real).
Embora eles não sejam mais usados em computadores desktop, o padrão C destina-se a suportar até arquiteturas pequenas e especializadas. Ainda existem sistemas embarcados sendo desenvolvidos com CPUs de 8 ou 16 bits, por exemplo.
fonte
size_t
também deve ser capaz de lidar com isso? Ou matrizes dinâmicas em algum segmento distante ainda seriam limitadas à indexação em seu segmento?str
funções e Borland mesmo para asmem
funções (memset
,memcpy
,memmove
). Isso significava que você poderia sobrescrever parte da memória quando o deslocamento excedeu, o que foi divertido de depurar em nossa plataforma incorporada.Eu imaginaria (e isso vale para todos os nomes de tipos) que transmite melhor suas intenções no código.
Por exemplo, embora
unsigned short
ewchar_t
sejam do mesmo tamanho no Windows (acho), usar emwchar_t
vez deunsigned short
mostra a intenção de usá-lo para armazenar um caractere amplo, em vez de apenas um número arbitrário.fonte
wchar_t
é muito maior do que uma,unsigned short
portanto, usar um para o outro seria errôneo e criaria uma séria (e moderna) preocupação de portabilidade, enquanto a portabilidade se preocupa entresize_t
euintptr_t
parece estar nas terras distantes de 1980, algo (facada aleatória no escuro sobre a data, lá)size_t
euintptr_t
ainda tem usos implícitos em seus nomes.Olhando para trás e para frente, e lembrando que várias arquiteturas excêntricas estavam espalhadas pela paisagem, tenho certeza de que elas estavam tentando envolver todos os sistemas existentes e também fornecer todos os possíveis sistemas futuros.
Tão certo, da maneira como as coisas se resolveram, até agora não precisávamos de tantos tipos.
Mas mesmo no LP64, um paradigma bastante comum, precisávamos de size_t e ssize_t para a interface de chamada do sistema. Pode-se imaginar um sistema legado ou futuro mais restrito, em que o uso de um tipo completo de 64 bits é caro e eles podem querer realizar operações de E / S maiores que 4 GB, mas ainda possuem indicadores de 64 bits.
Acho que você deve se perguntar: o que pode ter sido desenvolvido, o que pode vir no futuro. (Talvez indicadores de 128 bits do sistema distribuído na Internet, mas não mais que 64 bits em uma chamada do sistema, ou talvez até mesmo um limite "legado" de 32 bits. :-) Imagem de que sistemas legados podem obter novos compiladores C .. .
Além disso, observe o que existia na época. Além dos zilhões de modelos de memória em modo real 286, e os mainframes CDC de 60 bits / ponteiro de 18 bits? E a série Cray? Não importa ILP64, LP64, LLP64 normal. (Eu sempre pensei que a Microsoft era pretensiosa com o LLP64, deveria ter sido o P64.) Certamente posso imaginar um comitê tentando cobrir todas as bases ...
fonte
Isso implica que intptr_t sempre deve substituir size_t e vice-versa.
fonte