Pourquoi / quand utiliser` intptr t ' pour le type-casting en C?

j'ai une question concernant l'utilisation de intptr_t vs. long int. J'ai observé que les adresses mémoire incrémentantes (par exemple via l'arithmétique manuelle des pointeurs) diffèrent par type de données. Par exemple, incrémenter un pointeur de char ajoute 1 à l'adresse mémoire, alors que incrémenter un pointeur int ajoute 4, 8 pour un double, 16 pour un double long, etc...

Au début, je faisais quelque chose comme ceci:

char myChar, *pChar;
float myFloat, *pFloat;

pChar = &myChar;
pFloat = &myFloat;

printf( "pChar:  %dn", ( int )pChar );
printf( "pFloat: %dn", ( int )pFloat );

pChar++;
pFloat++;

printf( "and then after incrementing,:nn" );
printf( "pChar:  %dn", (int)pChar );
printf( "pFloat:    %dn", (int)pFloat );

qui a compilé et exécuté, mais XCode m'a donné des avertissements pour mon typecasting: "Cast de pointeur d'entier de taille différente."

après quelques googling et binginging (ce dernier mot est-il encore utilisé?), J'ai vu certaines personnes vous recommandons d'utiliser intptr_t:

#include <stdint.h>

...

printf( "pChar:  %ldn", ( intptr_t )pChar );
printf( "pFloat: %ldn", ( intptr_t )pFloat );

qui résout en effet les erreurs. J'ai donc pensé que je devrais désormais utiliser intptr_t pour les pointeurs de typographie... Mais après un peu de remue-ménage, j'ai découvert que je pouvais résoudre le problème en remplaçant simplement int avec long int:

printf( "pChar:  %ldn", ( long int )pChar );
printf( "pFloat: %ldn", ( long int )pFloat );

alors ma question est, pourquoi est intptr_t utile, et quand doit-il utilisé? Il semble superflu dans ce cas. Clairement, les adresses mémoire pour myChar et myFloat étaient juste trop grands pour tenir dans un int... donc typecasting long int s résolu le problème.

Est-il que, parfois, les adresses mémoire sont trop gros pour les long int? Maintenant que j'y pense, je suppose que c'est possible si vous avez > 4 Go de RAM, auquel cas mémoire les adresses peuvent dépasser 2^32 - 1 (valeur maximale pour les longues adresses non signées)... mais C a été créé bien avant que ce soit imaginable, Non? Ou étaient-ils que de prescience?

Merci!

42
demandé sur grisaitis 2011-06-13 07:16:56

4 réponses

voici le truc: sur certaines plateformes,int la bonne taille, mais sur d'autres, long est la bonne taille. Comment savez-vous qui est celui que vous devez utiliser? Tu ne le fais pas. On peut avoir raison, mais la norme ne donne aucune garantie quant à laquelle elle serait (si elle l'est). Ainsi, le standard fournit un type qui est défini pour être la taille correcte, indépendamment de quelle plate-forme vous êtes sur. Là où avant, il fallait écrire:

#ifdef PLATFORM_A
  typedef long intptr;
#else
  typedef int intptr;
#endif

Maintenant vous venez écrire:

#include <stdint.h>

et il couvre tellement plus de cas. Imaginez la spécialisation de l'extrait ci-dessus pour chaque plate-forme votre code fonctionne.

35
répondu Chris Lutz 2011-06-13 04:05:26

intptr_t est une nouvelle invention, créée après avoir imaginé des adresses mémoire 64 bits et même 128 bits.

Si vous jamais besoin de lancer un pointeur dans un type entier,toujours utiliser intptr_t. Faire quoi que ce soit d'autre causera des problèmes inutiles pour les personnes qui ont besoin de porter votre code à l'avenir.

il a fallu beaucoup de temps pour éliminer tous les bogues avec cela dans les programmes comme Mozilla/Firefox quand les gens ont voulu compiler sur 64-bit Linux.

45
répondu Zan Lynx 2011-06-13 03:21:49

tout d'Abord, intptr_t est-ce seulement pour les pointeurs de données (pas de fonctions) et n'est pas garanti d'exister.

alors, non, vous ne devriez pas l'utiliser pour l'impression. %p pour que. Vous avez juste à lancer votre pointeur à (void*) et là vous allez.

Il n'est pas bon pour l'arithmétique / accès aux différents octets. Cast to (unsigned char*) à la place.

intptr_t est vraiment pour les rares occasions que vous avez à interpréter les pointeurs comme des entiers (qu'ils ne le sont pas vraiment). Ne pas si vous ne devez pas.

9
répondu Jens Gustedt 2011-06-13 09:29:04

Vous pourrait rendre votre vie plus facile en utilisant le p spécificateur de conversion:

printf("%p\n", (void *)foo);

aussi, la façon portable d'imprimer une variable de type (u)intptr_t est d'utiliser le PRI*PTR macros à partir d' inttypes.h; le suivant est équivalent à l'utilisation de p sur ma plate-forme (32 bits):

printf("%08" PRIxPTR "\n", (uintptr_t)(void *)foo);

Le jette à void * sont nécessaires pour une portabilité complète, mais peuvent être omis sur les plateformes avec des représentations de pointeur uniformes.

8
répondu Christoph 2011-06-13 09:32:45