Вопрос

Я пытаюсь адаптировать существующий код к 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-разрядной машине и как можно более чисто.Есть какие - нибудь идеи ?

Это было полезно?

Решение

Использование 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 как ваш целочисленный тип.

Несколько ответов указывали на 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.hinttypes.h предоставляет услуги по stdint.h, что необычно, но стандарт C99 гласит [t] он заголовок <inttypes.h> включает в себя заголовок <stdint.h> и расширяет его за счет дополнительных возможностей, предоставляемых размещенными реализациями) и используйте макросы PRIxxx в ваших строках формата.

Кроме того, мои комментарии строго применимы к C, а не к C ++, но ваш код находится в подмножестве C ++, которое переносимо между C и C ++.Велика вероятность, что мои комментарии применимы.

  1. #include <stdint.h>
  2. Использование uintptr_t стандартный тип, определенный во включенном стандартном заголовочном файле.

Я думаю, что "значение" void * в данном случае является общим дескриптором.Это не указатель на значение, это само значение.(Просто так получилось, что void* используется программистами на C и C ++ именно так.)

Если он содержит целочисленное значение, то лучше бы оно было в пределах целочисленного диапазона!

Вот простой рендеринг в integer:

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.
*/

Заслуга принадлежит коммиттерам.

Лучшее, что можно сделать, это избегать преобразования из типа указателя в типы, не являющиеся указателями.Однако в вашем случае это явно невозможно.

Как все уже говорили, uintptr_t - это то, что вы должны использовать.

Это Ссылка содержит хорошую информацию о преобразовании в 64-битный код.

Существует также хорошее обсуждение этого вопроса на комп.std.c

С тех пор как 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");
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top