Преобразование указателя в целое число - PullRequest
75 голосов
/ 30 сентября 2008

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

Ответы [ 10 ]

70 голосов
/ 27 октября 2014

Я бы сказал, что это современный C ++ способ.

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

EDIT

правильный тип для целого числа

, поэтому правильный способ сохранить указатель в виде целого числа - использовать типы 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

Смежные вопросы

66 голосов
/ 30 сентября 2008

Используйте 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 http://msinttypes.googlecode.com/svn/trunk/stdint.h" rel = "noreferrer"> здесь или использовать переносную версию http://www.azillionmonkeys.com/qed/pstdint.h" rel = "noreferrer" > здесь .

41 голосов
/ 30 сентября 2008

'size_t' и 'ptrdiff_t' необходимы для соответствия вашей архитектуре (какой бы она ни была). Поэтому, я думаю, вместо использования int вы должны использовать size_t, который в 64-битной системе должен быть 64-битным.

В этом обсуждении unsigned int vs size_t более подробно.

16 голосов
/ 30 сентября 2008

Используйте uintptr_t в качестве целочисленного типа.

7 голосов
/ 30 сентября 2008

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

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

4 голосов
/ 10 ноября 2011

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

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

Вот простой рендеринг в целое число:

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

Это должно только дать предупреждение.

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

Я сталкивался с этим вопросом при изучении исходного кода 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.
*/

Кредит достается коммиттерам.

1 голос
/ 30 сентября 2008

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

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

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

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

0 голосов
/ 17 марта 2018

Поскольку 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");
...