Я пытаюсь адаптировать существующий код к 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-битной машине и был как можно более чистым. Есть идеи ?
size_t
неработающей - сегментированная память i386. Хотя 32-битной машине,sizeof
возвращается2
дляsize_t
. Ответ Алекса ниже кажется правильным. Ответ Алекс иuintptr_t
работает почти везде, и теперь это стандарт. Он обеспечивает обработку C ++ 11 и даже дает защиту заголовка C ++ 03.Ответы:
Используйте
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
файла от Microsoft отсюда или использовать переносную отсюда .источник
<cstdint>
или загрузка соответствующего исправления,cstdint
если вы загружаете файлstdint.h
.cstdint
частью стандарта C ++, как и все имена типов, определенные в нем. Это действительно подходит для указанных тегов. ... Я не согласен с определением типов вручную, но это может быть необходимо при работе с компиляторами, которые этого не делают.Я бы сказал, что это современный способ 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
Связанные вопросы
источник
uintptr_t
вместоsize_t
, то зачем это нужноreinterpret_cast
? Кажется, что это простоstatic_cast
, поскольку стандарт специально предоставляет совместимые типы данных ...static_cast
может преобразовать тип или, если это указатель, может выполнять корректировку указателя, если тип нуждается в этом.reinterpret_cast
на самом деле просто меняет тип базового паттерна памяти (без мутаций). чтобы уточнить: здесьstatic_cast
ведет себя идентично."size_t" и "ptrdiff_t" необходимы для соответствия вашей архитектуре (какой бы она ни была). Поэтому я думаю, что вместо использования int вы должны иметь возможность использовать size_t, который в 64-битной системе должен быть 64-битного типа.
Это обсуждение unsigned int vs size_t более подробно.
источник
size_t
является то, что он должен содержать любой результатsizeof()
. Это не обязательно делает его 64-битным на x64. см. такжеsize_t
может безопасно хранить значение указателя, не являющегося членом. См. En.cppreference.com/w/cpp/types/size_t .Используйте
uintptr_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 $
Вы заметите, что в месте вызова (in
main()
) есть проблема - преобразование целого числа в указатель без приведения. Вам нужно будет проанализировать свое использованиеfunction()
во всех его случаях, чтобы увидеть, как ему передаются значения. Код внутри меняfunction()
работал бы, если бы звонки были написаны:unsigned long i = 0x2341; function(1, &i);
Поскольку ваши, вероятно, написаны по-другому, вам необходимо просмотреть точки, в которых вызывается функция, чтобы убедиться, что имеет смысл использовать значение, как показано. Не забывайте, вы можете обнаружить скрытую ошибку.
Кроме того, если вы собираетесь форматировать значение
void *
параметра (как преобразованное), внимательно посмотрите на<inttypes.h>
заголовок (вместоstdint.h
-inttypes.h
предоставляет услугиstdint.h
, что необычно, но стандарт C99 говорит, что [t] он заголовок<inttypes.h>
включает заголовок<stdint.h>
и расширяет его дополнительными возможностями, предоставляемыми размещенными реализациями ) и используйте макросы PRIxxx в строках вашего формата.Кроме того, мои комментарии строго применимы к C, а не к C ++, но ваш код находится в подмножестве C ++, которое переносимо между C и C ++. Шансы на то, что мои комментарии применимы, весьма велики.
источник
#include <stdint.h>
uintptr_t
стандартный тип, определенный во включенном стандартном файле заголовка.источник
Я столкнулся с этим вопросом при изучении исходного кода 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. */
Кредит принадлежит коммиттерам.
источник
Лучше всего избегать преобразования типа указателя в типы, не являющиеся указателями. Однако в вашем случае это явно невозможно.
Как все говорили, uintptr_t - это то, что вам следует использовать.
Эта ссылка содержит полезную информацию о преобразовании в 64-битный код.
Это также хорошо обсуждается на comp.std.c
источник
Я думаю, что «значение» void * в данном случае - это общий дескриптор. Это не указатель на значение, это само значение. (Именно так void * используется программистами на C и C ++.)
Если он содержит целочисленное значение, оно должно быть в целочисленном диапазоне!
Вот простой рендеринг в целое число:
int x = (char*)p - (char*)0;
Он должен только предупреждать.
источник
Так
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");
источник