Преобразование указателя в целое число
Я пытаюсь адаптировать существующий код к 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-битной машине и был максимально чистым. Любая идея?
Ответы
Ответ 1
Используйте 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 и включите туда, где вам это нужно.
В качестве альтернативы вы можете загрузить версию stdint.h
Microsofts из здесь или использовать переносимый из здесь.
Ответ 2
Я бы сказал, что это современный 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
Смежные вопросы
Ответ 3
'size_t' и 'ptrdiff_t' должны соответствовать вашей архитектуре (независимо от того, что это такое). Поэтому я считаю, что вместо использования "int" вы должны использовать "size_t", который в 64-битной системе должен быть 64-разрядным типом.
Это обсуждение unsigned int vs size_t немного подробнее.
Ответ 4
Используйте uintptr_t
как целочисленный тип.
Ответ 5
Несколько ответов указывают на 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 говорит, что заголовок <inttypes.h>
включает заголовок <stdint.h>
и расширяет его с помощью
дополнительные возможности, предоставляемые размещенными реализациями) и использовать макросы PRIxxx в строках формата.
Кроме того, мои комментарии строго применимы к C, а не к С++, но ваш код находится в подмножестве С++, который переносится между C и С++. Шансы справедливы, что мои комментарии применимы.
Ответ 6
-
#include <stdint.h>
- Используйте стандартный
uintptr_t
стандартный тип, указанный в включенном стандартном файле заголовка.
Ответ 7
Я думаю, что "значение" void * в этом случае является общим дескриптором.
Это не указатель на значение, это само значение.
(Это как раз то, как void * используется программистами C и С++.)
Если он удерживает целочисленное значение, он лучше всего находиться в целочисленном диапазоне!
Здесь легко преобразование в целое число:
int x = (char*)p - (char*)0;
Он должен давать только предупреждение.
Ответ 8
Я столкнулся с этим вопросом, изучая исходный код 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.
*/
Кредит отправляется коммиттерам.
Ответ 9
Лучше всего сделать это, чтобы избежать преобразования из типа указателя в типы без указателей.
Однако это явно невозможно в вашем случае.
Как говорили все, uintptr_t - это то, что вы должны использовать.
Эта ссылка имеет хорошую информацию о преобразовании в 64-разрядный код.
Существует также хорошее обсуждение этого вопроса на comp.std.c
Ответ 10
Поскольку 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");