O que é o tipo de dados uintptr_t

Respostas:

199

uintptr_té um tipo inteiro não assinado capaz de armazenar um ponteiro de dados. O que normalmente significa que é do mesmo tamanho que um ponteiro.

É opcionalmente definido nos padrões C ++ 11 e posteriores.

Um motivo comum para querer um tipo inteiro que possa conter o tipo de ponteiro de uma arquitetura é executar operações específicas de número inteiro em um ponteiro ou ocultar o tipo de um ponteiro, fornecendo-o como um "identificador" inteiro.

Edit: Note que Steve Jessop tem alguns detalhes adicionais muito interessantes (que eu não roubarei) em outra resposta aqui para vocês, tipos pedantes :)

Drew Dormann
fonte
53
Observe que size_tsomente é necessário o tamanho do objeto maior e pode ser menor que um ponteiro. Isto seria esperado em arquiteturas segmentadas como o 8086 (16 bits size_t, mas 32 bits void*)
MSalters
3
para representar a diferença entre dois ponteiros, você tem ptrdiff_t. uintptr_tnão foi feito para isso.
jalf
3
@ jalf: Por diferença, sim, mas por distância , você deseja um tipo não assinado.
Drew Dormann 04/12/2009
A definição de uintptr_t aparentemente não é obrigatória (portanto não padrão), mesmo em C ++ 11! cplusplus.com/reference/cstdint (Eu recebi a dica da resposta de Steve Jessop)
Antonio
2
@ MarcusJ unsigned intgeralmente não é grande o suficiente. Mas pode ser grande o suficiente. Esse tipo existe especificamente para remover todos os "pressupostos" .
Drew Dormann
239

A primeira coisa, no momento em que a pergunta foi feita, uintptr_tnão estava em C ++. Está em C99, em <stdint.h>, como um tipo opcional. Muitos compiladores C ++ 03 fornecem esse arquivo. Também está em C ++ 11, em <cstdint>, onde novamente é opcional e se refere a C99 para a definição.

Em C99, é definido como "um tipo inteiro não assinado com a propriedade que qualquer ponteiro válido para anular pode ser convertido para esse tipo, depois convertido de volta para ponteiro para anular, e o resultado será comparado ao ponteiro original".

Entenda isso como o que diz. Não diz nada sobre tamanho.

uintptr_tpode ter o mesmo tamanho de a void*. Pode ser maior. Pode ser concebivelmente menor, embora essa implementação em C ++ seja perversa. Por exemplo, em alguma plataforma hipotética em que void*há 32 bits, mas apenas 24 bits de espaço de endereço virtual são usados, você pode ter 24 bits uintptr_tque atendam aos requisitos. Não sei por que uma implementação faria isso, mas o padrão permite isso.

Steve Jessop
fonte
8
Obrigado pelo "<stdint.h>". Meu aplicativo não foi compilado devido à declaração uintptr_t. Mas quando li seu comentário, adicionei "#include <stdint.h>" e sim, agora funciona. Obrigado!
JavaRunner 12/02
2
Para alocar memória Alinhados , entre outros usos, por exemplo?
legends2k
4
Outro caso de uso comum de converter um ponteiro para um int é criar um identificador opaco que oculta o ponteiro. Isso é útil para retornar uma referência a um objeto das APIs em que você deseja manter o objeto privado na biblioteca e impedir que os aplicativos tenham acesso a ele. A aplicação é então forçado a usar a API para executar qualquer operação sobre o objeto
Joel Cunningham
3
@ JoelCunningham: isso funciona, mas não é diferente de usar void*. No entanto, afeta possíveis direções futuras, especialmente se você quiser alterar para usar algo que realmente é apenas um identificador inteiro, e não um ponteiro convertido.
Steve Jessop
2
@CiroSantilli 事件 事件 2016 六四 事件 法轮功 Um caso de uso comum é passar apenas um int para uma API que espera um nulo * para dados genéricos. Salva as teclas digitadas typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;. Em vez disso: callback.dataPtr = (void*)val. Por outro lado, é claro que você obtém void*e precisa devolvê-lo int.
Francesco Dondi
19

É um tipo inteiro não assinado, exatamente do tamanho de um ponteiro. Sempre que você precisar fazer algo incomum com um ponteiro - como, por exemplo, inverter todos os bits (não pergunte o porquê), você o converte uintptr_te o manipula como um número inteiro usual, depois lança de volta.

dente afiado
fonte
6
Claro que você poderia fazer isso, mas isso seria um comportamento indefinido. Acredito que a única coisa que você pode fazer com o resultado de um elenco para uintptr_t é repassá-lo inalterado e devolvê-lo - tudo o resto é UB.
sleske
Há momentos em que você precisa brincar com os bits e isso normalmente gera erros de compilador. Um exemplo comum é impor memória alinhada de 16 bytes para certos aplicativos críticos de vídeo e desempenho. stackoverflow.com/questions/227897/…
Nuvem
3
@lesleske isso não é verdade. Nas máquinas que possuem tipos auto-alinhados, os dois bits menos significativos de um ponteiro serão zero (porque os endereços são múltiplos de 4 ou 8). Já vi programas que exploram esta com dados da compressa ..
saadtaame
3
@saadtaame: Eu só apontam para que este é UB acordo com o padrão C . Isso não significa que não possa ser definido em alguns sistemas - compiladores e tempos de execução são livres para definir um comportamento específico para algo que é UB no padrão C. Portanto, não há contradição :-).
sleske
2
Não é necessariamente exatamente o tamanho de um ponteiro. Todas as garantias padrão são que a conversão de um void*valor de ponteiro para uintptr_te para trás produz um void*valor que se compara igual ao ponteiro original. uintptr_tgeralmente é do mesmo tamanho que void*, mas isso não é garantido, nem existe garantia de que os bits do valor convertido tenham algum significado específico. E não há garantia de que ele possa reter um valor convertido de ponteiro para função sem perda de informações. Finalmente, não é garantido que exista.
Keith Thompson
15

Já existem muitas boas respostas para a parte "what is uintptr_t data type". Vou tentar abordar o "para que ele pode ser usado?" parte neste post.

Principalmente para operações bit a bit em ponteiros. Lembre-se de que em C ++ não é possível executar operações bit a bit em ponteiros. Por razões, consulte Por que você não pode executar operações bit a bit no ponteiro em C e existe uma maneira de contornar isso?

Portanto, para realizar operações bit a bit em ponteiros, seria necessário converter ponteiros para digitar unitpr_t e, em seguida, executar operações bit a bit.

Aqui está um exemplo de uma função que eu acabei de escrever para fazer bit a bit exclusivo ou de 2 ponteiros para armazenar em uma lista vinculada ao XOR, para que possamos percorrer as duas direções como uma lista duplamente vinculada, mas sem a penalidade de armazenar 2 ponteiros em cada nó .

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }
BGR
fonte
1
além da aritmética de bits, também é bom se você quiser ter semântica baseada em endereços, em vez de contagens de objetos.
Alex
4

Correndo o risco de obter outro crachá Necromancer, gostaria de adicionar um uso muito bom para uintptr_t (ou mesmo intptr_t) e que esteja escrevendo código incorporado testável. Eu escrevo principalmente código incorporado direcionado a vários processadores de braço e atualmente tensilica. Elas possuem várias larguras de barramento nativas e a tensilica é na verdade uma arquitetura de Harvard com barramentos de código e dados separados que podem ter larguras diferentes. Eu uso um estilo de desenvolvimento orientado a testes para grande parte do meu código, o que significa que eu faço testes de unidade para todas as unidades de código que escrevo. O teste de unidade no hardware de destino real é um aborrecimento, por isso geralmente escrevo tudo em um PC baseado em Intel, seja no Windows ou Linux usando Ceedling e GCC. Dito isto, muitos códigos incorporados envolvem manipulação de endereços e manipulação de bits. A maioria das minhas máquinas Intel são de 64 bits. Portanto, se você for testar o código de manipulação de endereço, precisará de um objeto generalizado para fazer as contas. Assim, o uintptr_t oferece a você uma maneira independente de depurar seu código antes da tentativa de implantar no hardware de destino. Outro problema é que algumas máquinas ou mesmo modelos de memória em alguns compiladores, ponteiros de função e ponteiros de dados têm larguras diferentes. Nessas máquinas, o compilador pode nem mesmo permitir a conversão entre as duas classes, mas o uintptr_t deve ser capaz de manter uma ou outra.

bd2357
fonte
Não tenho certeza se relevante ... Você já tentou "typedefs opacos"?
Vídeo
@sleske Eu gostaria que estivesse disponível em C. Mas ter stdint.h é melhor que nada. (Também deseja enums onde digitado mais forte, mas a maioria dos depuradores fazer um bom trabalho de descobrir-los de qualquer maneira)
bd2357