Conversion d’un pointeur en entier

J’essaie d’adapter un code existant à une machine 64 bits. Le problème principal est que dans une fonction, le codeur précédent utilise un argument void * qui est converti en type approprié dans la fonction elle-même. Un court exemple:

void function(MESSAGE_ID id, void* param) { if(id == FOO) { int real_param = (int)param; // ... } } 

Bien sûr, sur une machine 64 bits, je reçois l’erreur suivante:

 error: cast from 'void*' to 'int' loses precision 

Je voudrais corriger cela pour qu’il fonctionne toujours sur une machine 32 bits et aussi proprement que possible. Une idée ?

Utilisez intptr_t et uintptr_t .

Pour vous assurer qu’il est défini de manière portable, vous pouvez utiliser le code suivant:

 #if defined(__BORLANDC__) typedef unsigned char uint8_t; typedef __int64 int64_t; typedef unsigned long uintptr_t; #elif defined(_MSC_VER) typedef unsigned char uint8_t; typedef __int64 int64_t; #else #include  #endif 

Placez-le simplement dans un fichier .h et incluez-le où vous en avez besoin.

Sinon, vous pouvez télécharger la version Microsoft du fichier stdint.h à partir d’ ici ou en utiliser un portable.

Je dirais que c’est le moyen C ++ moderne.

 #include  void *p; auto i = reinterpret_cast(p); 

EDIT :

Le bon type à l’entier

Ainsi, le bon moyen de stocker un pointeur en tant uintptr_t consiste à utiliser les types uintptr_t ou intptr_t . (Voir aussi les types de cppreference integeger pour C99 ).

ces types sont définis dans pour C99 et dans l’espace de noms std pour C ++ 11 dans (voir les types entiers pour C ++ ).

Version C ++ 11 (et versions ultérieures)

 #include  std::uintptr_t i; 

Version C ++ 03

 extern "C" { #include  } uintptr_t i; 

Version C99

 #include  uintptr_t i; 

L’opérateur de casting correct

En C, il n’y a qu’une seule dissortingbution et l’utilisation de la dissortingbution C en C ++ est mal vue (ne l’utilisez donc pas en C ++). En C ++, il y a différents moulages. reinterpret_cast est la dissortingbution correcte pour cette conversion (voir aussi ici ).

Version C ++ 11

 auto i = reinterpret_cast(p); 

Version C ++ 03

 uintptr_t i = reinterpret_cast(p); 

Version C

 uintptr_t i = (uintptr_t)p; // C Version 

questions connexes

  • Quel est le type de données uintptr_t

‘size_t’ et ‘ptrdiff_t’ sont nécessaires pour correspondre à votre architecture (quelle qu’elle soit). Par conséquent, je pense qu’au lieu d’utiliser ‘int’, vous devriez pouvoir utiliser ‘size_t’, qui sur un système 64 bits devrait être de type 64 bits.

Cette discussion unsigned int vs size_t va un peu plus en détail.

Utilisez uintptr_t comme type d’entier.

Plusieurs réponses ont désigné uintptr_t et #include comme “la” solution. C’est, je suggère, une partie de la réponse, mais pas toute la réponse. Vous devez également regarder où la fonction est appelée avec l’ID de message de FOO.

Considérez ce code et cette compilation:

 $ cat kk.c #include  static void function(int n, void *p) { unsigned long z = *(unsigned long *)p; printf("%d - %lu\n", n, z); } int main(void) { function(1, 2); return(0); } $ rmk kk gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith \ -Wcast-qual -Wssortingct-prototypes -Wmissing-prototypes \ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk kk.c: In function 'main': kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast $ 

Vous remarquerez qu’il y a un problème à l’emplacement appelant (dans main() ) – en convertissant un entier en un pointeur sans fonte. Vous allez devoir parsingr votre function() dans tous ses usages pour voir comment les valeurs lui sont transmises. Le code dans ma function() fonctionnerait si les appels étaient écrits:

 unsigned long i = 0x2341; function(1, &i); 

Étant donné que les vôtres sont probablement écrits différemment, vous devez examiner les points sur lesquels la fonction est appelée pour vous assurer qu’il est judicieux d’utiliser la valeur indiquée. N’oubliez pas, vous pouvez trouver un bug latent.

En outre, si vous envisagez de formater la valeur du paramètre void * (converti), examinez attentivement l’en-tête (au lieu de stdint.hinttypes.h fournit les services de stdint.h , qui est inhabituel, mais le standard C99 dit [t] que l’en-tête inclut l’en-tête et l’étend aux fonctionnalités supplémentaires fournies par les implémentations hébergées ) et utilise les macros PRIxxx dans vos chaînes de format.

De plus, mes commentaires sont ssortingctement applicables à C plutôt qu’à C ++, mais votre code se trouve dans le sous-ensemble de C ++ portable entre C et C ++. Les chances sont bonnes pour que mes commentaires s’appliquent.

Je pense que la “signification” de void * dans ce cas est un handle générique. Ce n’est pas un pointeur sur une valeur, c’est la valeur elle-même. (Il se trouve que void * est utilisé par les programmeurs C et C ++.)

Si elle contient une valeur entière, mieux vaut être dans la plage entière!

Voici un rendu facile en nombre entier:

 int x = (char*)p - (char*)0; 

Il ne faut que donner un avertissement.

  1. #include
  2. Utilisez le type standard uintptr_t défini dans le fichier d’en-tête standard inclus.

La meilleure chose à faire est d’éviter de convertir un type de pointeur en un type sans pointeur. Cependant, cela n’est clairement pas possible dans votre cas.

Comme tout le monde l’a dit, le uintptr_t est ce que vous devez utiliser.

Ce lien contient de bonnes informations sur la conversion au code 64 bits.

Il y a aussi une bonne discussion à ce sujet sur comp.std.c

Je suis tombé sur cette question en étudiant le code source de SQLite .

Dans le sqliteInt.h , il y a un paragraphe de code défini comme une macro convertie entre un entier et un pointeur. L’auteur a fait une très bonne déclaration en soulignant qu’il devrait s’agir d’un problème dépendant du compilateur, puis a implémenté la solution pour prendre en compte la plupart des compilateurs populaires.

 #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) #elif !defined(__GNUC__) /* Works for comstackrs other than LLVM */ # define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ # define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif 

Et voici une citation du commentaire pour plus de détails:

 /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one comstackr ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of comstackrs. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all comstackrs, or ** if it is available, it requires an #include of specific headers ** that vary from one machine to the next. ** ** Ticket #3860: The llvm-gcc-4.2 comstackr from Apple chokes on ** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). ** So we have to define the macros in different ways depending on the ** comstackr. */ 

Le crédit va aux committers.

Etant donné que uintptr_t n’est pas uintptr_t présent en C ++ / C ++ 11 , s’il s’agit d’une conversion unidirectionnelle, vous pouvez considérer uintmax_t , toujours défini dans .

 auto real_param = reinterpret_cast(param); 

Pour jouer en toute sécurité, on pourrait append n’importe où dans le code une assertion:

 static_assert(sizeof (uintmax_t) >= sizeof (void *) , "No suitable integer type for conversion from pointer type");