web-dev-qa-db-fra.com

À quoi sert intptr_t?

Je sais que c'est un type entier qui peut être casté vers/depuis le pointeur sans perte de données, mais pourquoi voudrais-je jamais faire cela? Quel est l'avantage d'un type entier sur void* pour tenir le pointeur et THE_REAL_TYPE* pour l'arithmétique des pointeurs?

MODIFIER
La question marquée comme "déjà posée" ne répond pas à cela. La question se pose si vous utilisez intptr_t en remplacement général de void* est une bonne idée, et les réponses semblent "ne pas utiliser intptr_t", donc ma question est toujours valable: quel serait un bon cas d'utilisation pour intptr_t?

24
Baruch

La raison principale, vous ne pouvez pas effectuer d'opération au niveau du bit sur un void *, mais vous pouvez faire de même sur un intptr_t.

À plusieurs reprises, lorsque vous devez effectuer une opération au niveau du bit sur une adresse, vous pouvez utiliser intptr_t.

Cependant, pour les opérations au niveau du bit, la meilleure approche consiste à utiliser l'équivalent unsigned, uintptr_t.

Comme mentionné dans autre réponse par @ chux , la comparaison de pointeurs est un autre aspect important.

En outre, FWIW, selon C11 norme, §7.20.1.4,

Ces types sont facultatifs.

15
Sourav Ghosh

À quoi sert intptr_t?

Exemple d'utilisation: comparaison de commande.
Comparer les indicateurs de l'égalité n'est pas un problème.
D'autres opérations de comparaison comme >, <= peut être UB. C11dr §6.5.8/5 Opérateurs relationnels.
Convertissez donc en intptr_t premier.

[Modifier] Nouvel exemple: triez un tableau de pointeurs par valeur de pointeur.

int ptr_cmp(const void *a, const void *b) {
  intptr_t ia = (intptr) (*((void **) a));
  intptr_t ib = (intptr) (*((void **) b));
  return (ia > ib) - (ia < ib);
}

void *a[N];
...
qsort(a, sizeof a/sizeof a[0], sizeof a[0], ptr_cmp);

[Ancien exemple] Exemple d'utilisation: testez si un pointeur appartient à un tableau de pointeurs.

#define N  10
char special[N][1];

// UB as testing order of pointer, not of the same array, is UB.
int test_special1(char *candidate) {
  return (candidate >= special[0]) && (candidate <= special[N-1]);
}

// OK - integer compare
int test_special2(char *candidate) {
  intptr_t ca = (intptr_t) candidate;
  intptr_t mn = (intptr_t) special[0];
  intptr_t mx = (intptr_t) special[N-1];
  return (ca >= mn) && (ca <= mx);
}

Comme commenté par @ M.M , le code ci-dessus peut ne pas fonctionner comme prévu. Mais au moins ce n'est pas UB. - juste une fonctionnalité non portable. J'espérais l'utiliser pour résoudre ce problème .

7
chux

Il y a aussi une considération sémantique.

UNE void* est censé pointer vers quelque chose. Malgré l'aspect pratique moderne, un pointeur n'est pas une adresse mémoire. D'accord, il en contient généralement/probablement/toujours (!), Mais ce n'est pas un nombre. C'est un pointeur. Cela fait référence à une chose.

UNE intptr_t ne fait pas. C'est une valeur entière, qui peut être convertie en/à partir d'un pointeur en toute sécurité afin que vous puissiez l'utiliser pour des API anciennes, en l'intégrant dans un argument de fonction pthread, des choses comme ça.

C'est pourquoi vous pouvez faire plus de choses sur un intptr_t que sur un void*, et pourquoi vous devriez vous documenter en utilisant le type approprié pour le travail.

En fin de compte, presque tout pourrait être un entier (rappelez-vous, votre ordinateur fonctionne sur des nombres!). Les pointeurs auraient pu être des entiers. Mais ce n'est pas le cas. Ce sont des pointeurs, car ils sont destinés à un usage différent. Et, théoriquement, ils pourraient être autre chose que des nombres.

Le type uintptr_t est très utile lors de l'écriture de code de gestion de mémoire. Ce type de code veut parler à ses clients en termes de pointeurs génériques (void *), mais fait en interne toutes sortes d'arithmétique sur les adresses.

Vous pouvez faire la même chose en fonctionnant en termes de char *, mais pas tout, et le résultat ressemble à pré-Ansi C.

Tous les codes de gestion de la mémoire n'utilisent pas uintptr_t - à titre d'exemple, le code du noyau BSD définit un vm_offset_t avec des propriétés similaires. Mais si vous écrivez par exemple un paquet debug malloc, pourquoi inventer votre propre type?

Il est également utile lorsque% p est disponible dans votre printf et que vous écrivez du code qui doit imprimer des variables intégrales de la taille d'un pointeur en hexadécimal sur diverses architectures.

Je trouve intptr_t plutôt moins utile, sauf peut-être en tant que station intermédiaire lors de la conversion, pour éviter l'avertissement redoutable concernant la modification de la signature et de la taille entière dans la même distribution. (L'écriture de code portable qui passe -Wall -Werror sur toutes les architectures pertinentes peut être un peu difficile.)

6
Arlie Stephens