Conversion d'un pointeur en un 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 vide* qui est converti en type approprié dans la fonction elle-même. Un bref exemple:
void function(MESSAGE_ID id, void* param)
{
if(id == FOO) {
int real_param = (int)param;
// ...
}
}
bien sûr, sur une machine 64 bits, j'obtiens l'erreur:
error: cast from 'void*' to 'int' loses precision
je voudrais corriger cela pour qu'il fonctionne encore sur une machine 32 bits et aussi proprement que possible. Une idée ?
10 réponses
utiliser intptr_t
et uintptr_t
.
pour s'assurer qu'il est défini d'une manière portable, vous pouvez utiliser le code comme ceci:
#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 <stdint.h>
#endif
placez ça dans certains .h de fichiers et d'inclure partout où vous en avez besoin.
alternativement, vous pouvez télécharger la version de Microsoft du fichier stdint.h
de ici ou utiliser une portable de ici .
je dirais que c'est la voie c++ moderne.
#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);
MODIFIER :
le type correct à L'entier
, donc la bonne façon de stocker un pointeur d'entier est d'utiliser le uintptr_t
ou intptr_t
. (Voir aussi dans CPP reference integeger types for C99 ).
ces types sont définis dans <stdint.h>
pour C99 et dans l'espace de noms std
pour le C++11 dans <cstdint>
(voir les types integer for C++ ).
C++11 (et versions ultérieures)
#include <cstdint>
std::uintptr_t i;
C++03 Version
extern "C" {
#include <stdint.h>
}
uintptr_t i;
C99 Version
#include <stdint.h>
uintptr_t i;
Le bon opérateur de coulée
en C il n'y a qu'un seul moulage et utiliser le C moulé en C++ est mal vu (donc ne l'utilisez pas en C++). En C++, il y a différents casts. reinterpret_cast
est la fonte correcte pour cette conversion (Voir aussi ici ).
C++11 Version
auto i = reinterpret_cast<std::uintptr_t>(p);
C++03 Version
uintptr_t i = reinterpret_cast<uintptr_t>(p);
C Version
uintptr_t i = (uintptr_t)p; // C Version
Related Questions
' size_t 'et' ptrdiff_t ' sont nécessaires pour correspondre à votre architecture (quelle qu'elle soit). Par conséquent, je pense que plutôt que d'utiliser 'int', vous devriez être en mesure d'utiliser 'size_t', qui sur un système 64 bits devrait être un type 64 bits.
Cette discussion unsigned int vs size_t va dans un peu plus en détail.
plusieurs réponses ont indiqué uintptr_t
et #include <stdint.h>
comme" la " solution. C'est, à mon avis, Une partie de la réponse, mais pas la totalité. Vous devez également regarder où la fonction est appelée avec le message ID de FOO.
Considérer ce code et à la compilation:
$ cat kk.c
#include <stdio.h>
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 -Wstrict-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 d'appel (dans main()
) - convertir un entier en pointeur sans lancer. Vous allez avoir besoin d'analyser votre function()
dans tous ses usages à voir comment les valeurs sont transmises. Le code dans mon function()
fonctionnerait si les appels étaient écrits:
unsigned long i = 0x2341;
function(1, &i);
Depuis les vôtres sont probablement écrit différemment, vous devez examiner les points où la fonction est appelée pour s'assurer qu'il est logique d'utiliser la valeur. N'oubliez pas, il se peut que vous trouviez un bug latent.
aussi, si vous allez formater le valeur du paramètre void *
(telle que convertie), regardez attentivement l'en-tête <inttypes.h>
(au lieu de stdint.h
- inttypes.h
fournit les services de stdint.h
, ce qui est inhabituel, mais la norme C99 dit [l] 'en-tête <inttypes.h>
inclut l'en-tête <stdint.h>
et l'étend avec
les fonctionnalités supplémentaires fournies par les implémentations hébergées ) et utilisez les macros PRIxxx dans vos chaînes de format.
Aussi, mes commentaires sont strictement applicable à C plutôt qu'à C++, mais votre code est dans le sous-ensemble de C++ qui est portable entre C et C++. Il y a de bonnes chances que mes commentaires s'appliquent.
je pense que le "sens" de void* dans ce cas est un générique de la poignée. Il n'est pas un pointeur vers une valeur, c'est la valeur elle-même. (Il se trouve que c'est comme ça que void* est utilisé par les programmeurs C et c++.)
S'il contient une valeur entière, il vaut mieux qu'il soit à portée entière!
voici le rendu facile à Entier:
int x = (char*)p - (char*)0;
il ne doit donner qu'un avertissement.
-
#include <stdint.h>
- utiliser
uintptr_t
type standard défini dans le fichier d'en-tête standard inclus.
la meilleure chose à faire est d'éviter de passer du type pointeur aux types non-pointeur. Cependant, ce n'est clairement pas possible dans votre cas.
comme tout le monde l'a dit, l'uintptr_t est ce que vous devez utiliser.
Ce lien a la bonne info au sujet de la conversion de code 64 bits.
il y a aussi une bonne discussion de cela sur comp.MST.c
j'ai rencontré cette question en étudiant le code source de SQLite .
Dans le sqliteInt.h , il y a un paragraphe de code défini une macro convertir entre entier et pointeur. L'auteur a fait une très bonne déclaration d'abord en soulignant qu'il devrait être un problème dépendant du compilateur et a ensuite mis en œuvre la solution pour rendre compte de la plupart des compilateurs populaires là-bas.
#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 compilers 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 compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, 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 compiler 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
** compiler.
*/
le crédit va aux committers.
depuis uintptr_t
est pas garanti d'être là en C++ / C++11 , si c'est une conversion à Sens Unique , vous pouvez considérer uintmax_t
, toujours défini dans <cstdint>
.
auto real_param = reinterpret_cast<uintmax_t>(param);
pour jouer prudemment, on pourrait ajouter n'importe où dans le code une affirmation:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");