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 ?

72
demandé sur PierreBdR 2008-09-30 17:43:47

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 .

64
répondu Milan Babuškov 2015-08-28 08:19:21

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

65
répondu Alex 2017-05-23 12:10:30

' 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.

42
répondu Richard Corden 2017-05-23 12:26:22

utilisez uintptr_t comme votre type entier.

15
répondu moonshadow 2015-08-28 08:19:48

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.

7
répondu Jonathan Leffler 2014-08-31 00:08:37

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.

5
répondu Craig Hicks 2011-11-10 06:47:52
  1. #include <stdint.h>
  2. utiliser uintptr_t type standard défini dans le fichier d'en-tête standard inclus.
4
répondu Michael S. 2010-12-04 21:19:19

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

1
répondu Benoit 2008-09-30 14:05:07

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.

1
répondu B.Mr.W. 2017-03-25 17:11:55

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");
0
répondu Antonio 2018-03-19 16:40:30