357 votes

Qu'est-ce que le type de données uintptr_t ?

Qu'est-ce que uintptr_t et à quoi peut-il servir ?

6 votes

Des détails supplémentaires sur ce type ainsi que sur d'autres types apparentés sont disponibles ici : opengroup.org/onlinepubs/000095399/basedefs/stdint.h.html

2 votes

fr.cppreference.com/w/cpp/types/integer listes std::uintptr_t y std::intptr_t une option C++11.

313voto

Steve Jessop Points 166970

Tout d'abord, au moment où la question a été posée, uintptr_t n'était pas dans C++. C'est dans C99, dans <stdint.h> comme un type optionnel. De nombreux compilateurs C++03 fournissent ce fichier. Il est également présent dans C++11, dans le fichier <cstdint> où, là encore, elle est facultative, et qui renvoie à la C99 pour la définition.

En C99, il est défini comme "un type d'entier non signé ayant 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 égal au pointeur original".

Prenez ça pour ce que ça veut dire. Ça ne dit rien sur la taille.

uintptr_t peut être de la même taille qu'un void* . Elle pourrait être plus grande. Il pourrait être concevable qu'il soit plus petit, bien qu'une telle implémentation en C++ s'approche de la perversion. Par exemple, sur une plateforme hypothétique où void* est de 32 bits, mais que seuls 24 bits de l'espace d'adressage virtuel sont utilisés, vous pourriez avoir une adresse virtuelle de 24 bits. uintptr_t ce qui satisfait à l'exigence. Je ne sais pas pourquoi une implémentation ferait cela, mais la norme le permet.

17 votes

Merci pour le "<stdint.h>". Mon application n'a pas compilé à cause de la déclaration uintptr_t. Mais quand j'ai lu votre commentaire j'ai ajouté "#include <stdint.h>" et oui maintenant ça marche. Merci !

4 votes

A allouer de la mémoire alignée parmi d'autres usages, par exemple ?

5 votes

Un autre cas d'utilisation courant du casting d'un pointeur vers un int est la création d'un handle opaque qui cache le pointeur. C'est utile pour renvoyer une référence à un objet à partir d'API où vous voulez garder l'objet privé pour la bibliothèque et empêcher les applications d'y avoir accès. L'application est alors obligée d'utiliser l'API pour effectuer toute opération sur l'objet.

271voto

Drew Dormann Points 25025

uintptr_t est un type d'entier non signé qui est capable de stocker un pointeur de données. Ce qui signifie généralement qu'il a la même taille qu'un pointeur.

Il est défini de manière facultative dans les normes C++11 et ultérieures.

Une raison courante de vouloir un type entier qui peut contenir le type de pointeur d'une architecture est d'effectuer des opérations spécifiques aux entiers sur un pointeur, ou de masquer le type d'un pointeur en le fournissant comme un "handle" entier.

67 votes

Notez que size_t doit seulement être suffisante pour contenir la taille de l'objet le plus grand, et peut être plus petite qu'un pointeur. On s'attendrait à cela sur des architectures segmentées comme le 8086 (16 bits, 10 bits, 10 bits et 10 bits). size_t mais 32 bits void* )

4 votes

Pour représenter la différence entre deux pointeurs, vous avez ptrdiff_t . uintptr_t n'est pas fait pour ça.

4 votes

@jalf : Pour la différence, oui, mais pour distance vous voulez un type non signé.

32voto

sharptooth Points 93379

C'est un type d'entier non signé qui a exactement la taille d'un pointeur. Lorsque vous devez faire quelque chose d'inhabituel avec un pointeur - comme par exemple inverser tous les bits (ne demandez pas pourquoi), vous le transformez en uintptr_t et le manipule comme un nombre entier habituel, puis le renvoie.

6 votes

Bien sûr, vous pourriez le faire, mais ce serait bien sûr un comportement non défini. Je crois que la seule chose que l'on puisse faire avec le résultat d'un cast vers uintptr_t est de le transmettre inchangé et de le re-transférer - tout le reste est UB.

0 votes

Il y a des moments où vous devez jouer avec les bits, et cela générerait normalement des erreurs de compilation. Un exemple courant consiste à imposer une mémoire alignée sur 16 octets pour certaines applications vidéo et de performance critique. stackoverflow.com/questions/227897/

4 votes

@sleske ce n'est pas vrai. Sur les machines qui ont des types auto-alignés, les deux bits les moins significatifs d'un pointeur vont être zéro (parce que les adresses sont des multiples de 4 ou 8). J'ai vu des programmes qui exploitent cela pour compresser des données

20voto

BGR Points 529

Il existe déjà de nombreuses bonnes réponses à la question "Qu'est-ce que le type de données uintptr_t ? Je vais essayer de répondre à la partie "à quoi il peut servir" dans ce billet.

Principalement pour les opérations bit à bit sur les pointeurs. Rappelez-vous qu'en C++, on ne peut pas effectuer d'opérations bit à bit sur les pointeurs. Pour les raisons, voir Pourquoi ne peut-on pas faire d'opérations par bit sur un pointeur en C, et existe-t-il un moyen de contourner ce problème ?

Ainsi, afin d'effectuer des opérations par bit sur des pointeurs, il faudrait convertir les pointeurs en type uintptr_t et ensuite effectuer des opérations par bit.

Voici un exemple d'une fonction que je viens d'écrire pour faire un "bitwise exclusive or" de 2 pointeurs à stocker dans une liste liée XOR de sorte que nous pouvons traverser dans les deux sens comme une liste doublement liée mais sans la pénalité de stocker 2 pointeurs dans chaque noeud.

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }

1 votes

En dehors de l'arithmétique des bits, c'est également intéressant si vous voulez avoir une sémantique basée sur les adresses plutôt que sur le nombre d'objets.

0 votes

Hah, la liste chaînée Xor est aussi la raison pour laquelle je peux trouver cette raison.

12voto

bd2357 Points 152

Au risque de me voir attribuer un autre badge de Nécromancien, je voudrais ajouter une très bonne utilisation pour uintptr_t (ou même intptr_t ) et c'est l'écriture de code embarqué testable.

J'écris principalement du code embarqué destiné à divers processeurs arm et actuellement tensilica. Ceux-ci ont différentes largeurs de bus natifs 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 largeurs différentes.

J'utilise un style de développement piloté par les tests pour une grande partie de mon code, ce qui signifie que je réalise des tests unitaires pour toutes les unités de code que j'écris. Les tests unitaires sur le matériel cible réel sont une corvée, donc j'écris généralement tout sur un PC basé sur Intel, soit sous Windows, soit sous Linux en utilisant Ceedling et GCC.

Ceci étant dit, une grande partie du code embarqué implique des manipulations de bits et d'adresses. La plupart de mes machines Intel sont en 64 bits. Donc si vous voulez tester un code de manipulation d'adresse, vous avez besoin d'un objet généralisé sur lequel faire des calculs. Ainsi le uintptr_t vous offrent un moyen indépendant de la machine pour déboguer votre code avant d'essayer de le déployer sur le matériel cible.

Un autre problème est que pour certaines machines ou même certains modèles de mémoire sur certains compilateurs, les pointeurs de fonctions et les pointeurs de données ont des largeurs différentes. Sur ces machines, le compilateur peut même ne pas autoriser le casting entre les deux classes, mais uintptr_t devrait être capable de tenir l'un ou l'autre.

-- Editer --

Cela a été signalé par @chux, cela ne fait pas partie de la norme et les fonctions ne sont pas des objets en C. Cependant, cela fonctionne généralement et comme beaucoup de gens ne connaissent pas ces types, je laisse généralement un commentaire expliquant la ruse. Autres recherches dans SO on uintptr_t fournira des explications supplémentaires. De plus, nous faisons des choses dans les tests unitaires que nous ne ferions jamais en production, car il est bon de casser les choses.

0 votes

Pas sûr que ce soit pertinent... Avez-vous essayé "opaque typedefs" ? Vide : youtube.com/watch?v=jLdSjh8oqmE

0 votes

@sleske J'aimerais que cela soit disponible en C. Mais avoir stdint.h est mieux que rien. (J'aimerais aussi que les enums soient mieux typés, mais la plupart des débogueurs font un bon travail pour les comprendre de toute façon).

0 votes

"mais uintptr_t devrait être capable de contenir l'un ou l'autre." --> Pas tout à fait. uintptr_t bon pour convertir en/depuis un void * y void * vers/depuis des pointeurs d'objets. Un pointeur de fonction ne peut pas faire l'aller-retour avec une fonction void * - (par exemple, pointeur de fonction plus grand que void * ) et par extension, ne pas s'intégrer dans un uintptr_t .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X