it-swarm.com.de

Einen Zeiger in eine Ganzzahl konvertieren

Ich versuche, einen vorhandenen Code an eine 64-Bit-Maschine anzupassen. Das Hauptproblem besteht darin, dass der vorherige Codierer in einer Funktion ein Argument void * verwendet, das in der Funktion selbst in einen geeigneten Typ konvertiert wird. Ein kurzes Beispiel:

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

Auf einer 64-Bit-Maschine bekomme ich natürlich den Fehler:

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

Ich möchte dies korrigieren, damit es auf einer 32-Bit-Maschine immer noch so sauber wie möglich funktioniert. Irgendeine Idee ?

73
PierreBdR

Verwenden Sie intptr_t und uintptr_t.

Um sicherzustellen, dass es auf tragbare Weise definiert ist, können Sie Code wie folgt verwenden:

#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

Platzieren Sie das einfach in einer .h-Datei und fügen Sie sie dort ein, wo Sie es benötigen.

Alternativ können Sie die Microsoft-Version der stdint.h-Datei von hier herunterladen oder eine tragbare Version von hier verwenden.

65
Milan Babuškov

Ich würde sagen, dies ist der moderne C++ - Weg.

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

EDIT:

Der richtige Typ für die Ganzzahl

der richtige Weg, um einen Zeiger als Ganzzahl zu speichern, ist die Verwendung der Typen uintptr_t oder intptr_t. (Siehe auch in cppreference integer-Typen für C99 ).

diese Typen sind in <stdint.h> für C99 und im Namespace std für C++ 11 in <cstdint> definiert (siehe Integer-Typen für C++ ).

C++ 11 (und höher) Version

#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;

Der richtige Casting-Operator

In C gibt es nur einen Cast und die Verwendung des C-Casts in C++ ist verpönt (verwenden Sie ihn also nicht in C++). In C++ gibt es verschiedene Besetzungen. reinterpret_cast ist die korrekte Umwandlung für diese Konvertierung (Siehe auch hier ).

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

Verwandte Fragen

70
Alex

'size_t' und 'ptrdiff_t' werden benötigt, um zu Ihrer Architektur zu passen (was auch immer es ist). Daher denke ich, anstatt 'int' zu verwenden, sollten Sie 'size_t' verwenden können, das auf einem 64-Bit-System ein 64-Bit-Typ sein sollte.

Diese Diskussion unsigned int vs size_t geht etwas detaillierter vor.

41
Richard Corden

Verwenden Sie uintptr_t als Ganzzahl.

16
moonshadow

Mehrere Antworten haben auf uintptr_t und #include <stdint.h> als "die" Lösung hingewiesen. Das ist, denke ich, ein Teil der Antwort, aber nicht die ganze Antwort. Sie müssen auch prüfen, wo die Funktion mit der Nachrichten-ID von FOO aufgerufen wird.

Betrachten Sie diesen Code und diese Zusammenstellung:

$ 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
$

Sie werden feststellen, dass an der aufrufenden Stelle (in main()) ein Problem vorliegt - das Konvertieren einer Ganzzahl in einen Zeiger ohne Umwandlung. Sie müssen Ihre function() in all ihren Verwendungen analysieren, um zu sehen, wie Werte an sie übergeben werden. Der Code in meiner function() würde funktionieren, wenn die Aufrufe geschrieben wurden:

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

Da Ihre Werte wahrscheinlich anders geschrieben werden, müssen Sie die Punkte überprüfen, an denen die Funktion aufgerufen wird, um sicherzustellen, dass es sinnvoll ist, den angezeigten Wert zu verwenden. Vergessen Sie nicht, dass Sie möglicherweise einen latenten Fehler finden.

Wenn Sie den Wert des Parameters void * (wie konvertiert) formatieren möchten, sollten Sie sich den Header <inttypes.h> genau ansehen (anstelle von stdint.h - inttypes.h werden die Dienste von stdint.h bereitgestellt. Dies ist ungewöhnlich, der C99-Standard sagt jedoch [t Der Header <inttypes.h> enthält den Header <stdint.h> und erweitert ihn um zusätzliche Funktionen, die von gehosteten Implementierungen) bereitgestellt werden, und verwendet die PRIxxx-Makros in Ihren Formatzeichenfolgen.

Meine Kommentare sind auch streng auf C und nicht auf C++ anwendbar, aber Ihr Code befindet sich in der Teilmenge von C++, die zwischen C und C++ portierbar ist. Die Chancen stehen gut, dass meine Kommentare zutreffen.

7
  1. #include <stdint.h>
  2. Verwenden Sie den uintptr_t-Standardtyp, der in der enthaltenen Standardheaderdatei definiert ist.
4
Michael S.

Ich denke, die "Bedeutung" von void * ist in diesem Fall ein generischer Handle . Es ist kein Zeiger auf einen Wert, es ist der Wert selbst ..__ C- und C++ - Programmierer.)

Wenn es einen ganzzahligen Wert enthält, sollte es besser im ganzzahligen Bereich liegen!

Hier ist ein einfaches Rendern in Ganzzahl:

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

Es sollte nur eine Warnung geben.

4
Craig Hicks

Am besten vermeiden Sie es, vom Zeigertyp in Nicht-Zeigertypen zu konvertieren .. Dies ist jedoch in Ihrem Fall eindeutig nicht möglich.

Wie alle sagten, sollten Sie das uintptr_t verwenden.

Dieser Link hat gute Informationen zum Konvertieren in 64-Bit-Code.

Es gibt auch eine gute Diskussion darüber auf comp.std.c

1
Benoit

Ich bin auf diese Frage gestoßen, als ich den Quellcode von SQLite studierte. 

In sqliteInt.h ist ein Codeabschnitt definiert, der eine Makrokonvertierung zwischen Ganzzahl und Zeiger definiert. Der Autor gab zunächst eine sehr gute Aussage ab und wies darauf hin, dass es sich hierbei um ein vom Compiler abhängiges Problem handeln sollte. Anschließend wurde die Lösung implementiert, um die meisten der populären Compiler zu berücksichtigen. 

#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

Und hier ist ein Zitat des Kommentars für weitere Details: 

/*
** 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.
*/

Die Gutschrift geht an die Auftraggeber. 

1
B.Mr.W.

Da uintptr_tnicht garantiert ist, dass es in C++/C++ 11 vorhanden ist, können Sie, wenn dies eine Einwegkonvertierung ist, uintmax_t berücksichtigen, die immer in <cstdint> definiert ist.

auto real_param = reinterpret_cast<uintmax_t>(param);

Um auf Nummer sicher zu gehen, könnte man irgendwo im Code eine Assertion hinzufügen:

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