À quoi sert-il uintptr_t
et à quoi peut-il servir?
À quoi sert-il uintptr_t
et à quoi peut-il servir?
Réponses:
uintptr_t
est un type entier non signé capable de stocker un pointeur de données. Ce qui signifie généralement que c'est la même taille qu'un pointeur.
Il est éventuellement défini dans les normes C ++ 11 et ultérieures.
Une raison courante de vouloir un type entier pouvant contenir le type de pointeur d'une architecture est d'effectuer des opérations spécifiques à un entier sur un pointeur, ou d'obscurcir le type d'un pointeur en le fournissant comme un "handle" entier.
Edit: Notez que Steve Jessop a quelques détails supplémentaires très intéressants (que je ne volerai pas) dans une autre réponse ici pour vous les types pédants :)
size_t
ne doit être suffisant que pour contenir la taille du plus grand objet et peut être plus petit qu'un pointeur. Cela serait attendu sur des architectures segmentées comme le 8086 (16 bits size_t
, mais 32 bits void*
)
ptrdiff_t
. uintptr_t
n'est pas fait pour ça.
unsigned int
n'est généralement pas assez grand. Mais il pourrait être assez grand. Ce type existe spécifiquement pour supprimer tous les "présupposés" .
La première chose, au moment où la question a été posée, uintptr_t
n'était pas en C ++. C'est en C99, en <stdint.h>
, comme type facultatif. De nombreux compilateurs C ++ 03 fournissent ce fichier. C'est aussi en C ++ 11, en <cstdint>
, où là encore c'est facultatif, et qui fait référence à C99 pour la définition.
Dans C99, il est défini comme "un type entier non signé avec la propriété que tout pointeur valide vers void peut être converti en ce type, puis reconverti en pointeur vers void, et le résultat sera comparable à celui du pointeur d'origine".
Prenez cela pour signifier ce qu'il dit. Cela ne dit rien sur la taille.
uintptr_t
peut avoir la même taille qu'un fichier void*
. Il pourrait être plus grand. Il pourrait en théorie être plus petit, bien qu'une telle implémentation C ++ approche de façon perverse. Par exemple, sur une plate-forme hypothétique où void*
32 bits, mais seulement 24 bits d'espace d'adressage virtuel sont utilisés, vous pourriez avoir un 24 bits uintptr_t
qui satisfait à l'exigence. Je ne sais pas pourquoi une implémentation ferait cela, mais la norme le permet.
void*
. Cependant, cela affecte les directions futures possibles, surtout si vous souhaitez changer pour utiliser quelque chose qui n'est vraiment qu'un handle entier, pas du tout un pointeur converti.
typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;
. Au lieu de cela: callback.dataPtr = (void*)val
. De l'autre côté, vous obtenez bien sûr void*
et devez le lancer à nouveau int
.
Il s'agit d'un type entier non signé exactement de la taille d'un pointeur. Chaque fois que vous devez faire quelque chose d'inhabituel avec un pointeur - comme par exemple inverser tous les bits (ne demandez pas pourquoi) vous le transformez uintptr_t
et le manipulez comme un nombre entier habituel, puis rétrogradez.
void*
valeur de pointeur vers uintptr_t
et en arrière produit une void*
valeur comparable au pointeur d'origine. uintptr_t
est généralement de la même taille que void*
, mais cela n'est pas garanti, et il n'y a aucune garantie que les bits de la valeur convertie ont une signification particulière. Et il n'y a aucune garantie qu'il puisse contenir une valeur de pointeur vers fonction convertie sans perte d'informations. Enfin, il n'est pas garanti d'exister.
Il existe déjà de nombreuses bonnes réponses à la partie "qu'est-ce que le type de données uintptr_t". Je vais essayer de répondre à la question "à quoi cela peut-il servir?" partie à ce poste.
Principalement pour les opérations au niveau du bit sur les pointeurs. N'oubliez pas qu'en C ++, on ne peut pas effectuer d'opérations au niveau du bit sur des pointeurs. Pour des raisons, voir Pourquoi ne pouvez-vous pas effectuer des opérations au niveau du bit sur un pointeur en C, et existe-t-il un moyen de contourner cela?
Ainsi, pour effectuer des opérations au niveau du bit sur des pointeurs, il faudrait transtyper des pointeurs pour taper unitpr_t, puis effectuer des opérations au niveau du bit.
Voici un exemple d'une fonction que je viens d'écrire pour faire exclusif au niveau du bit ou de 2 pointeurs à stocker dans une liste liée XOR afin que nous puissions parcourir dans les deux sens comme une liste doublement liée mais sans pénalité de stocker 2 pointeurs dans chaque nœud .
template <typename T>
T* xor_ptrs(T* t1, T* t2)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
}
Risquant d'obtenir un autre badge Necromancer, je voudrais en ajouter un très bon usage pour uintptr_t (ou même intptr_t) et qui est d'écrire du code embarqué testable. J'écris principalement du code embarqué destiné à différents processeurs bras et actuellement tensilica. Ceux-ci ont différentes largeurs de bus natives et le tensilica est en fait une architecture Harvard avec des bus de code et de données séparés qui peuvent être de différentes largeurs. J'utilise un style de développement piloté par les tests pour une grande partie de mon code, ce qui signifie que je fais des tests unitaires pour toutes les unités de code que j'écris. Les tests unitaires sur le matériel cible réel sont un problème, donc j'écris généralement tout sur un PC basé sur Intel sous Windows ou Linux en utilisant Ceedling et GCC. Cela étant dit, beaucoup de code intégré implique des manipulations de bits et des manipulations d'adresses. La plupart de mes machines Intel sont en 64 bits. Donc, si vous allez tester le code de manipulation d'adresse, vous avez besoin d'un objet généralisé pour faire des calculs. Ainsi, uintptr_t vous offre un moyen indépendant de la machine de déboguer votre code avant d'essayer de déployer sur le matériel cible. Un autre problème est pour certaines machines ou même les modèles de mémoire sur certains compilateurs, les pointeurs de fonction et les pointeurs de données sont de largeurs différentes. Sur ces machines, le compilateur peut même ne pas autoriser le transtypage entre les deux classes, mais uintptr_t devrait être capable de contenir l'une ou l'autre.