Question

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 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, le message d'erreur suivant s'affiche:

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?

Était-ce utile?

La solution

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 ce fichier dans un fichier .h et de l'inclure où vous en avez besoin.

Vous pouvez également télécharger la version du fichier stdint.h de Microsoft & # 8217; à partir de here ou utilisez un portable de ici .

Autres conseils

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 pour l'entier

donc la bonne façon de stocker un pointeur sous forme d’entier est d’utiliser les types uintptr_t ou intptr_t. (Voir également cppreference les 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 ++ ).

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

#include <cstdint>
std::uintptr_t i;

Version C ++ 03

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

uintptr_t i;

Version C99

#include <stdint.h>
uintptr_t i;

L'opérateur de casting approprié

En C, il n’existe qu’une seule conversion et son utilisation en C ++ est désapprouvée (ne l’utilisez donc pas en C ++). En C ++, il y a différents moulages. reinterpret_cast correspond à la conversion correcte pour cette conversion (voir aussi ici ).

Version C ++ 11

auto i = reinterpret_cast<std::uintptr_t>(p);

Version C ++ 03

uintptr_t i = reinterpret_cast<uintptr_t>(p);

Version C

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

Questions connexes

'size_t' et 'ptrdiff_t' sont obligatoires 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 de type 64 bits.

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

Utilisez uintptr_t comme type entier.

Plusieurs réponses ont désigné uintptr_t et #include <stdint.h> comme "la" solution. C’est, à mon avis, une partie de la réponse, mais pas la réponse dans son ensemble. 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 <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 constaterez qu'il y a un problème à l'emplacement de l'appelant (dans main()) & # 8212; convertir un entier en un pointeur sans transtypage. Vous allez devoir analyser votre function() dans toutes ses utilisations pour voir comment les valeurs lui sont transmises. Le code dans mon void * 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 bogue latent.

De même, si vous souhaitez formater la valeur du paramètre <inttypes.h> (tel que converti), examinez attentivement l'en-tête stdint.h (au lieu de inttypes.h & # 8212; <stdint.h> fournit les services de <=>, ce qui est inhabituel, mais la norme C99 dit [l] en-tête <=> inclut l’en-tête <=> et l’élargit avec fonctionnalités supplémentaires fournies par les implémentations hébergées ) et utilisez 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.

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

Je pense que le " signifiant " de void * est dans ce cas un descripteur générique. Ce n'est pas un pointeur sur une valeur, c'est la valeur elle-même. (C’est justement ainsi que void * est utilisé par les programmeurs C et C ++.)

S'il contient une valeur entière, il vaut mieux rester dans la plage de nombres entiers!

Voici un rendu facile en entier:

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

Cela ne devrait donner qu'un avertissement.

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

Dans le sqliteInt.h , il y a est 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 s’agissait d’un problème dépendant du compilateur, puis en implémentant 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 crédit va aux committers.

La meilleure chose à faire est d'éviter de convertir un type de pointeur en type 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.

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

Il existe également une bonne discussion à ce sujet sur comp. std.c

Dans la mesure où uintptr_t est non garanti en C ++ / C ++ 11 , le cas échéant. Vous pouvez considérer la conversion de manière uintmax_t, toujours définie dans <cstdint> .

auto real_param = reinterpret_cast<uintmax_t>(param);

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

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top