web-dev-qa-db-fra.com

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 * converti en un type approprié dans la fonction elle-même. Un petit 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'ai l'erreur:

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 ?

73
PierreBdR

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 <stdint.h>
#endif

Il suffit de placer cela dans un fichier .h et d'inclure où vous en avez besoin.

Vous pouvez également télécharger la version Microsoft du fichier stdint.h à partir de ici ou utiliser un fichier portable à partir de ici .

65
Milan Babuškov

Je dirais que c'est la manière moderne de C++.

#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);

MODIFIER:

Le type correct à l'entier

la bonne façon de stocker un pointeur en tant qu'entier consiste donc à utiliser les types uintptr_t ou intptr_t. (Voir aussi cppreference types entiers pour C99 ).

ces types sont définis dans <stdint.h> pour C99 et dans l'espace de noms std pour C++ 11 dans <cstdint> (voir types entiers pour C++ ).

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

#include <cstdint>
std::uintptr_t i;

C++ 03 Version

extern "C" {
#include <stdint.h>
}

uintptr_t i;

Version C99

#include <stdint.h>
uintptr_t i;

Le bon opérateur de casting

En C, il n'y a qu'une seule conversion et l'utilisation de la version C en C++ est désapprouvée (donc, ne l'utilisez pas en C++). En C++, il y a différents moulages. reinterpret_cast est la conversion 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

Questions connexes

70
Alex

'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 pouvoir utiliser "size_t", qui sur un système 64 bits devrait être un type 64 bits.

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

41
Richard Corden

Utilisez uintptr_t comme type entier.

16
moonshadow

Plusieurs réponses ont indiqué que uintptr_t et #include <stdint.h> étaient «la» solution. C’est, à mon avis, une partie de la réponse, mais pas la réponse dans son ensemble. Vous devez également rechercher l'endroit où la fonction est appelée avec l'ID de message de FOO.

Considérez ce code et cette 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 de l'appelant (dans main()): la conversion d'un entier en pointeur sans conversion. Vous allez devoir analyser votre function() dans toutes ses utilisations 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);

Etant donné que les vôtres sont probablement écrits différemment, vous devez vérifier les points où la fonction est appelée pour vous assurer qu'il est judicieux d'utiliser la valeur comme indiqué. N'oubliez pas que vous êtes peut-être en train de trouver un bug latent.

De même, si vous souhaitez formater la valeur du paramètre void * (converti), examinez 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 [t ] L'en-tête <inttypes.h> inclut l'en-tête <stdint.h> et l'étend avec des 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 strictement applicables au C plutôt qu'au C++, mais votre code est dans le sous-ensemble du C++ portable entre le C et le C++. Les chances sont bonnes à bonnes que mes commentaires s’appliquent.

7
Jonathan Leffler
  1. #include <stdint.h>
  2. Utilisez le type standard uintptr_t défini dans le fichier d'en-tête standard inclus.
4
Michael S.

Je pense que la "signification" de void * dans ce cas est un descripteur générique . Ce n'est pas un pointeur sur une valeur, mais bien la valeur elle-même . Programmeurs C et C++.)

Si elle contient une valeur entière, il vaut mieux qu'elle soit dans la plage des nombres entiers!

Voici un rendu facile en entier:

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

Il devrait seulement donner un avertissement.

4
Craig Hicks

La meilleure chose à faire est d'éviter de convertir le type de pointeur en type de non-pointeur ..__ Cependant, ce n'est clairement pas possible dans votre cas.

Comme tout le monde l’a dit, c’est le uintptr_t que vous devriez utiliser.

This link contient de bonnes informations sur la conversion en code 64 bits.

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

1
Benoit

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éfinissant une macro convertie entre un entier et un pointeur. L’auteur a fait une très bonne déclaration, soulignant d’abord que c’était un problème dépendant du compilateur, puis avait 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 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 mérite en revient aux committers. 

1
B.Mr.W.

Puisque uintptr_t est il n’est pas garanti qu’il existe en C++/C++ 11 , s’il s’agit d’une conversion à sens unique, vous pouvez envisager uintmax_t, toujours défini dans <cstdint> .

auto real_param = reinterpret_cast<uintmax_t>(param);

Pour jouer en toute sécurité, on peut ajouter n'importe où dans le code une assertion:

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