Преобразование указателя в целое число
Я пытаюсь адаптировать существующий код для 64-битной машины. Основная проблема заключается в том, что в одной функции предыдущий кодер использует аргумент void*, который преобразуется в подходящий тип в самой функции. Краткий пример:
void function(MESSAGE_ID id, void* param)
{
if(id == FOO) {
int real_param = (int)param;
// ...
}
}
Конечно, на 64-битной машине я получаю сообщение об ошибке:
error: cast from 'void*' to 'int' loses precision
Я хотел бы исправить это так, чтобы он все еще работал на 32-битной машине и как можно более чисто. Любая идея?
11 ответов
Использование intptr_t
а также uintptr_t
,
Чтобы убедиться, что он определен переносимым способом, вы можете использовать такой код:
#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
Просто поместите это в какой-нибудь файл.h и добавьте туда, где вам это нужно.
Кроме того, вы можете скачать версию Microsoft stdint.h
файл отсюда или используйте портативный отсюда.
Я бы сказал, что это современный способ C++.
#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);
РЕДАКТИРОВАТЬ:
Правильный тип для целого числа
поэтому правильный способ сохранить указатель в виде целого числа - это использовать uintptr_t
или же intptr_t
типы. (См. Также в целочисленных типах cppreference для C99).
эти типы определены в <stdint.h>
для C99 и в пространстве имен std
для C++11 в <cstdint>
(см. целочисленные типы для C++).
Версия C++11 (и выше)
#include <cstdint>
std::uintptr_t i;
Версия C++03
extern "C" {
#include <stdint.h>
}
uintptr_t i;
Версия C99
#include <stdint.h>
uintptr_t i;
Правильный оператор приведения
В C есть только одно приведение, и использование C приведено в C++ (не используйте его в C++). В C++ есть разные приведения. reinterpret_cast
правильный бросок для этого преобразования (см. также здесь).
Версия C++11
auto i = reinterpret_cast<std::uintptr_t>(p);
Версия C++03
uintptr_t i = reinterpret_cast<uintptr_t>(p);
C версия
uintptr_t i = (uintptr_t)p; // C Version
Смежные вопросы
'size_t' и 'ptrdiff_t' необходимы для соответствия вашей архитектуре (какой бы она ни была). Поэтому, я думаю, вместо использования int вы должны использовать size_t, который в 64-битной системе должен быть 64-битным.
В этом обсуждении unsigned int vs size_t более подробно.
Несколько ответов указали на uintptr_t
а также #include <stdint.h>
как "решение". Это, я полагаю, часть ответа, но не весь ответ. Вам также нужно посмотреть, где вызывается функция, с идентификатором сообщения FOO.
Рассмотрим этот код и компиляцию:
$ 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
$
Вы заметите, что есть проблема в месте вызова (в main()
) - преобразование целого числа в указатель без приведения. Вам нужно будет проанализировать свой function()
во всех его использованиях, чтобы увидеть, как ценности передаются ему. Код внутри моего function()
будет работать, если звонки были написаны:
unsigned long i = 0x2341;
function(1, &i);
Поскольку ваши, вероятно, написаны по-другому, вам нужно просмотреть точки, где вызывается функция, чтобы убедиться, что имеет смысл использовать значение, как показано. Не забывайте, что вы можете найти скрытую ошибку.
Кроме того, если вы собираетесь отформатировать значение void *
параметр (в пересчете), внимательно посмотрите на <inttypes.h>
заголовок (вместо stdint.h
- inttypes.h
предоставляет услуги stdint.h
, что необычно, но стандарт C99 гласит [t]he header <inttypes.h>
включает в себя заголовок <stdint.h>
и расширяет его дополнительными возможностями, предоставляемыми размещенными реализациями) и использует макросы PRIxxx в строках формата.
Кроме того, мои комментарии строго применимы к C, а не к C++, но ваш код находится в подмножестве C++, которое переносимо между C и C++. Скорее всего, мои комментарии применимы.
Я думаю, что "значение" void* в этом случае является общим дескриптором. Это не указатель на значение, это само значение. (Это как раз то, как void* используется программистами на C и C++.)
Если оно содержит целочисленное значение, лучше бы оно было в целочисленном диапазоне!
Вот простой рендеринг в целое число:
int x = (char*)p - (char*)0;
Стоит только дать предупреждение.
Я сталкивался с этим вопросом при изучении исходного кода SQLite.
В sqliteInt.h есть параграф кода, определяющий преобразование макроса между целым числом и указателем. Автор сделал очень хорошее заявление, сначала указав, что проблема должна зависеть от компилятора, а затем реализовал решение, учитывающее большинство популярных компиляторов.
#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
А вот цитата из комментария для более подробной информации:
/*
** 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.
*/
Кредит достается коммиттерам.
#include <stdint.h>
- использование
uintptr_t
стандартный тип, определенный во включенном стандартном заголовочном файле.
Лучше всего избегать преобразования из типа указателя в не указатель типа. Тем не менее, это явно не возможно в вашем случае.
Как все говорили, вы должны использовать uintptr_t.
Эта ссылка содержит полезную информацию о преобразовании в 64-битный код.
Это также хорошее обсуждение этого на comp.std.c
С С++11, для чего это стоит, предположим, что у вас нет заголовков, а затем определите:
template<bool B, class T, class F> struct cond { typedef T type; };
template<class T, class F> struct cond<false, T, F> { typedef F type;};
static constexpr unsigned int PS = sizeof (void *);
using uintptr_type = typename cond<
PS==sizeof(unsigned short), unsigned short ,
typename cond<
PS==sizeof(unsigned int), unsigned int,
typename cond<
PS==sizeof(unsigned long), unsigned long, unsigned long long>::type>::type>::type;
После этого вы можете сделать следующее:
static uintptr_type ptr_to_int(const void *pointer) {
return reinterpret_cast<uintptr_type>(pointer);
}
static void *int_to_ptr(uintptr_type integer) {
return reinterpret_cast<void *>(integer);
}
Поскольку uintptr_t
в C++/C++11 не гарантируется, что это одностороннее преобразование, которое вы можете рассмотреть uintmax_t
всегда определяется в <cstdint>
,
auto real_param = reinterpret_cast<uintmax_t>(param);
В целях безопасности можно добавить в любом месте кода утверждение:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");