Konwersja wskaźnika na liczbę całkowitą

Próbuję dostosować istniejący kod do 64-bitowej maszyny. Głównym problemem jest to, ż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 64-bitowej maszynie dostaję błąd:

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

Chciałbym to poprawić tak, aby nadal działało na maszynie 32-bitowej i tak czysto, jak to tylko możliwe. Jakiś pomysł ?

Author: PierreBdR, 2008-09-30

10 answers

Użyj intptr_t i uintptr_t.

Aby upewnić się, że jest zdefiniowany w sposób przenośny, możesz użyć kodu w następujący sposób:

#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 niektórych .plik h i dołącz tam, gdzie go potrzebujesz.

Alternatywnie możesz pobrać wersję pliku stdint.h firmy Microsoft z tutaj lub użyć przenośnego z tutaj .

 64
Author: Milan Babuškov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-28 08:19:21

Powiedziałbym, że jest to nowoczesny sposób C++.

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

EDIT :

Poprawny typ do liczby całkowitej

Więc właściwym sposobem przechowywania wskaźnika jako liczby całkowitej jest użycie typów uintptr_t lub intptr_t. (Zobacz także w cppreference integeger types for C99 ).

Typy te są zdefiniowane w <stdint.h> dla C99 oraz w przestrzeni nazw std dla C++11 w <cstdint> (Zobacz typy całkowite dla C++).

C++11 (i nowsze) Wersja

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

Prawidłowy operator odlewu

W C jest tylko jeden cast i używanie cast w C++ jest źle widziane(więc nie używaj go w C++). W C++ istnieją różne formy. reinterpret_castjest prawidłową obsadą dla tego nawrócenia (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);

C Wersja

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

Podobne Pytania

 65
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:30

'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ś być w stanie użyć 'size_t', który w systemie 64-bitowym powinien być typem 64-bitowym.

Ta dyskusja unsigned int vs size_t jest nieco bardziej szczegółowa.

 42
Author: Richard Corden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:22

Użyj uintptr_t jako typu integer.

 15
Author: moonshadow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-28 08:19:48

Kilka odpowiedzi wskazywało na uintptr_t i #include <stdint.h> jako "rozwiązanie". To jest, sugeruję, część odpowiedzi, ale nie cała odpowiedź. Musisz również spojrzeć na to, gdzie funkcja jest wywoływana z ID 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 istnieje problem w miejscu wywołania (w main()) - konwersja liczby całkowitej na wskaźnik bez obsady. Będziesz musiał przeanalizować swój function() we wszystkich jego zastosowaniach, aby zobaczyć, jak wartości są przekazywane do niego. Kod w moim function() zadział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 sensowne jest użycie wartości, jak pokazano. Nie zapominaj, że możesz znaleźć ukryty błąd.

Ponadto, jeśli zamierzasz sformatować wartość parametru void * (jako przekonwertowanego), przyjrzyj się uważnie nagłówkowi <inttypes.h> (zamiast stdint.h - inttypes.h świadczy usługi z stdint.h, co jest niezwykłe, ale standard C99 mówi [t] he header <inttypes.h> zawiera nagłówek <stdint.h> i rozszerza go o dodatkowe udogodnienia dostarczane przez hostowane implementacje ) i używać makr PRIxxx w ciągach formatujących.

Ponadto Moje komentarze są ściśle stosowane do C, a nie C++, ale twój kod jest w podzbiorze C++, który jest przenośny między C i C++. Szanse są słuszne, że moje komentarze mają zastosowanie.

 7
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-31 00:08:37

Myślę ,że" znaczenie " void* w tym przypadku jest ogólnym uchwytem. Nie jest wskaźnikiem do wartości, jest samą wartością. (Tak się składa, że void* jest używany przez programistów C i C++.)

Jeśli posiada wartość całkowitą, to lepiej, żeby była w zakresie liczby całkowitej!

Tutaj jest łatwe renderowanie do liczby całkowitej:

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

Powinno dać tylko Ostrzeżenie.

 5
Author: Craig Hicks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-10 06:47:52
  1. #include <stdint.h>
  2. Użyj standardowego typu uintptr_t zdefiniowanego w dołączonym standardowym pliku nagłówkowym.
 4
Author: Michael S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-04 21:19:19

Najlepszą rzeczą do zrobienia jest uniknięcie konwersji z typu wskaźnika do typu nie-wskaźnika. Nie jest to jednak możliwe w Twoim przypadku.

Jak wszyscy mówili, uintptr_t jest tym, czego powinieneś używać.

Ten link ma dobre informacje o konwersji na kod 64-bitowy.

Jest również dobra dyskusja na ten temat na comp.choroby weneryczne.c

 1
Author: Benoit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 14:05:07

Natknąłem się na to pytanie studiując kod źródłowy SQLite.

W sqliteInt.h , jest paragraf kodu zdefiniowany makro konwersja między liczbą całkowitą a wskaźnikiem. Autor wygłosił bardzo dobre stwierdzenie, 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 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 idzie do Komisji.

 1
Author: B.Mr.W.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-25 17:11:55

Ponieważ uintptr_t jest nie ma gwarancji, że będzie tam w C++ / C++11, jeśli jest to konwersja jednokierunkowa, możesz rozważyć uintmax_t, zawsze zdefiniowana w <cstdint>.

auto real_param = reinterpret_cast<uintmax_t>(param);

Aby grać bezpiecznie, można w dowolnym miejscu w kodzie dodać twierdzenie:

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");
 0
Author: Antonio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-19 16:40:30