Próbuję dostosować istniejący kod do maszyny 64-bitowej. Główny problem polega na tym, że w jednej funkcji poprzedni koder używa argumentu void *, który jest konwertowany na odpowiedni typ w samej funkcji. Krótki przykład:
void function(MESSAGE_ID id, void* param)
{
if(id == FOO) {
int real_param = (int)param;
// ...
}
}
Oczywiście na komputerze 64-bitowym pojawia się błąd:
error: cast from 'void*' to 'int' loses precision
Chciałbym to poprawić, aby nadal działało na komputerze 32-bitowym i tak czysto, jak to możliwe. Dowolny pomysł ?
size_t
niedziałającej pamięci jest segmentowana pamięć i386. Chociaż jest to maszyna 32-bitowa,sizeof
wraca2
dosize_t
. Odpowiedź Alex poniżej wydaje się poprawna. Odpowiedź Alexauintptr_t
działa prawie wszędzie i jest teraz standardem. Zapewnia obsługę C ++ 11, a nawet zapewnia ochronę nagłówka C ++ 03.Odpowiedzi:
Użyj
intptr_t
iuintptr_t
.Aby upewnić się, że jest zdefiniowany w przenośny sposób, możesz użyć takiego kodu:
#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
Po prostu umieść to w jakimś pliku .h i dołącz tam, gdzie tego potrzebujesz.
Alternatywnie możesz pobrać wersję
stdint.h
pliku firmy Microsoft stąd lub użyć wersji przenośnej stąd .źródło
<cstdint>
lub pobranie odpowiedniego,cstdint
jeśli pobierasz plikstdint.h
.cstdint
jest częścią standardu C ++, podobnie jak wszystkie zdefiniowane tam nazwy typów. Rzeczywiście jest to odpowiednie dla określonych tagów. ... Nie zgadzam się na ręczne definiowanie typów, ale może to być konieczne podczas pracy z kompilatorami, które tego nie robią.Powiedziałbym, że to nowoczesny sposób C ++.
#include <cstdint> void *p; auto i = reinterpret_cast<std::uintptr_t>(p);
EDYCJA :
Prawidłowy typ do liczby całkowitej
więc właściwym sposobem na przechowywanie wskaźnika jako liczby całkowitej jest użycie typów
uintptr_t
lubintptr_t
. (Zobacz także w typach całkowitych cppreference dla C99 ).te typy są zdefiniowane w
<stdint.h>
C99 i w przestrzeni nazwstd
dla C ++ 11 w<cstdint>
(zobacz typy całkowite dla C ++ ).Wersja C ++ 11 (i nowsze)
#include <cstdint> std::uintptr_t i;
Wersja C ++ 03
extern "C" { #include <stdint.h> } uintptr_t i;
Wersja C99
#include <stdint.h> uintptr_t i;
Właściwy operator rzutowania
W C jest tylko jedno rzutowanie i używanie rzutowania C w C ++ jest źle widziane (więc nie używaj go w C ++). W C ++ są różne rzuty.
reinterpret_cast
jest prawidłową obsadą dla tej konwersji (zobacz także tutaj ).Wersja C ++ 11
auto i = reinterpret_cast<std::uintptr_t>(p);
Wersja C ++ 03
uintptr_t i = reinterpret_cast<uintptr_t>(p);
Wersja C.
uintptr_t i = (uintptr_t)p; // C Version
Powiązane pytania
źródło
uintptr_t
zamiastsize_t
, to dlaczego to wymagareinterpret_cast
? Wydaje się, że to prostestatic_cast
, ponieważ standard wyraźnie zapewnia kompatybilne typy danych ...static_cast
może to przekonwertować typ lub jeśli jest to wskaźnik, może dokonać korekty wskaźnika, jeśli typ tego wymaga.reinterpret_cast
tak naprawdę po prostu zmienia typ podstawowego wzorca pamięci (bez mutacji). wyjaśnienie:static_cast
zachowuje się tutaj identycznie.„size_t” i „ptrdiff_t” są wymagane, aby pasowały do Twojej architektury (cokolwiek to jest). Dlatego myślę, że zamiast używać „int”, powinieneś móc użyć parametru „size_t”, który w systemie 64-bitowym powinien być typem 64-bitowym.
Ta dyskusja unsigned int vs size_t jest bardziej szczegółowa.
źródło
size_t
jest to, że musi zawierać wynik dowolnegosizeof()
. Niekoniecznie oznacza to 64 bity na x64. zobacz takżesize_t
może bezpiecznie przechowywać wartość wskaźnika niebędącego członkiem. Zobacz en.cppreference.com/w/cpp/types/size_t .Użyj
uintptr_t
jako typu liczby całkowitej.źródło
Kilka odpowiedzi wskazywały na
uintptr_t
i#include <stdint.h>
jako „” rozwiązania. To jest, jak sugeruję, część odpowiedzi, ale nie cała odpowiedź. Musisz również sprawdzić, gdzie wywoływana jest funkcja z identyfikatorem wiadomości FOO.Rozważ ten kod i kompilację:
$ 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 $
Zauważysz, że występuje problem w miejscu wywołania (in
main()
) - konwersja liczby całkowitej na wskaźnik bez rzutowania. Będziesz musiał przeanalizowaćfunction()
wszystkie jego zastosowania, aby zobaczyć, jak przekazywane są do niego wartości. Kod wewnątrz mojegofunction()
działałby, gdyby wywołania były napisane:unsigned long i = 0x2341; function(1, &i);
Ponieważ twoje są prawdopodobnie napisane inaczej, musisz przejrzeć punkty, w których funkcja jest wywoływana, aby upewnić się, że ma sens użycie wartości, jak pokazano. Nie zapominaj, że możesz znaleźć ukryty błąd.
Ponadto, jeśli zamierzasz sformatować wartość
void *
parametru (jako przekonwertowaną), spójrz uważnie na<inttypes.h>
nagłówek (zamiaststdint.h
-inttypes.h
zapewnia usługistdint.h
, co jest niezwykłe, ale standard C99 mówi, że [t] on nagłówek<inttypes.h>
zawiera nagłówek<stdint.h>
i rozszerza go o dodatkowe udogodnienia zapewniane przez hostowane implementacje ) i użyj makr PRIxxx w ciągach formatujących.Ponadto moje komentarze odnoszą się ściśle do C, a nie C ++, ale Twój kod należy do podzbioru C ++, który jest przenośny między C i C ++. Są duże szanse, że moje uwagi będą miały zastosowanie.
źródło
#include <stdint.h>
uintptr_t
standardowego typu zdefiniowanego w dołączonym standardowym pliku nagłówkowym.źródło
Natknąłem się na to pytanie, studiując kod źródłowy SQLite .
W sqliteInt.h znajduje się akapit kodu, w którym zdefiniowano makro konwertujące między liczbą całkowitą a wskaźnikiem. Autor złożył bardzo dobre oświadczenie, najpierw wskazując, że powinien to być problem zależny od kompilatora, a następnie zaimplementował rozwiązanie, aby uwzględnić większość popularnych kompilatorów.
#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
A oto cytat z komentarza po więcej szczegółów:
/* ** 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. */
Kredyt trafia do zatwierdzających.
źródło
Najlepszą rzeczą do zrobienia jest unikanie konwersji z typu wskaźnikowego na typy niebędące wskaźnikami. Jednak w twoim przypadku jest to oczywiście niemożliwe.
Jak wszyscy powiedzieli, uintptr_t jest tym, czego powinieneś używać.
To łącze zawiera dobre informacje na temat konwersji do kodu 64-bitowego.
Jest to również dobre omówienie w comp.std.c
źródło
Myślę, że „znaczenie” void * w tym przypadku jest ogólnym uchwytem. Nie jest wskaźnikiem do wartości, jest wartością samą w sobie. (Tak się składa, że void * jest używany przez programistów C i C ++).
Jeśli przechowuje wartość całkowitą, lepiej, aby mieścił się w zakresie liczb całkowitych!
Oto łatwe renderowanie do liczby całkowitej:
int x = (char*)p - (char*)0;
Powinien tylko dawać ostrzeżenie.
źródło
Ponieważ nie
uintptr_t
ma gwarancji, że będzie tam w C ++ / C ++ 11 , jeśli jest to konwersja jednokierunkowa, którą możesz rozważyćuintmax_t
, zawsze zdefiniowana w<cstdint>
.auto real_param = reinterpret_cast<uintmax_t>(param);
Aby być bezpiecznym, można dodać w dowolnym miejscu kodu asercję:
static_assert(sizeof (uintmax_t) >= sizeof (void *) , "No suitable integer type for conversion from pointer type");
źródło