Преобразование указателя в целое число

Я пытаюсь адаптировать существующий код к 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. Хотя sizeof является 32-битным компьютером, он возвращает 2 для size_t. Алекс ответ ниже кажется правильным. Ответ Алекса и uintptr_t работает практически везде и теперь является стандартом. Он обеспечивает обработку C++ 11 и даже дает защиту заголовка C++ 03.

jww 27.09.2016 20:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
89
1
170 004
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Ответ принят как подходящий

Используйте 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 из здесь или использовать переносную версию из здесь.

См. stackoverflow.com/questions/126279/… для информации о том, как получить stdint.h, который работает с MSVC (и, возможно, Borland).

Michael Burr 30.09.2008 18:55

Обе ссылки битые!

Antonio 17.03.2018 01:58

Этот ответ связан с C, но язык помечен как C++, поэтому это не тот ответ, который я искал.

HaseeB Mir 26.04.2018 15:51

@HaSeeBMiR Подходящим исправлением является переключение на <cstdint> или загрузка соответствующего cstdint, если вы загружаете stdint.h.

Justin Time - Reinstate Monica 13.11.2018 23:36

@JustinTime Но он все равно останется C++, добавление <cstdint> или любого файла заголовка c не делает язык c. это расширение компиляторов для поддержки старых файлов заголовков .час в современных компиляторах C++, поэтому C++ должен был поддерживать это и добавлять все файлы заголовков .час C как <c-header-file>. тем не менее, я бы предпочел, чтобы люди отвечали в соответствии с тегами, связанными с тем, что спросил OP.

HaseeB Mir 14.11.2018 00:44

@HaSeeBMiR Единственная причина, по которой ответ относится к C, а не к C++, заключается в том, что он использует заголовок C вместо эквивалентного заголовка C++. Препроцессор C является частью C++, а cstdint - частью стандарта C++, как и все имена типов, определенные в нем. Это действительно подходит для указанных тегов. ... Я не согласен с определением типов вручную, но это может быть необходимо при работе с компиляторами, которые этого не делают.

Justin Time - Reinstate Monica 15.11.2018 02:00

Этот ответ невероятно устарел. В наши дни просто #include <cstdint>.

Konrad Rudolph 01.12.2020 13:41

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

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

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

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

Это также хорошо обсуждается на comp.std.c

Несколько ответов указали на 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++. Шансы на то, что мои комментарии применимы, весьма велики.

Думаю, вы упустили суть моего вопроса. Код сохраняет значение целого числа в указателе. И эта часть кода делает противоположное (например, извлекает значение целого числа, которое было записано как как указатель).

PierreBdR 28.10.2014 11:11

@PierreBdR Тем не менее, он высказывает очень веское мнение. Не всегда так просто смотреть на код (в том числе когда компиляторы предупреждают об этом), который использует подписанный int, но используется для размера, и подумать, что можно изменить его на unsigned. К сожалению, это не всегда так просто. Вы должны подробно рассматривать каждый случай, если только вы не хотите вызвать потенциальные ошибки - и даже тонкие ошибки.

Pryftan 18.11.2019 01:06

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

Это обсуждение беззнаковое int против size_t более подробно.

Хотя size_t обычно достаточно велик, чтобы содержать указатель, это не всегда так. Было бы лучше найти заголовок stdint.h (если у вашего компилятора его еще нет) и использовать uintptr_t.

Michael Burr 01.10.2008 02:00

К сожалению, единственным ограничением size_t является то, что он должен содержать результат любого sizeof(). Это не обязательно делает его 64-битным на x64. смотрите также

Antoine 19.11.2013 14:44

size_tможет безопасно сохраняет значение указателя, не являющегося членом. См. en.cppreference.com/w/cpp/types/size_t.

AndyJost 09.04.2016 03:10

@AndyJost Нет, не может. Даже ваша собственная ссылка подтверждает это.

yyny 23.04.2016 03:45

@YoYoYonnY: «На многих платформах (исключение составляют системы с сегментированной адресацией) std :: size_t может безопасно хранить значение любого указателя, не являющегося членом, и в этом случае он является синонимом std :: uintptr_t». - о чем ты говоришь?

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

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

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

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

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

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

Я бы сказал, что это современный способ 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

Связанные вопросы

единственный ответ, в котором правильно упоминается reinterpret_cast

plasmacel 20.07.2015 13:07

Если вы хотели включить <cstdint>, вы, вероятно, также захотите использовать вместо него std :: uintptr_t.

linleno 20.11.2015 22:42

Потрясающе ... Актерский состав - это то, что я искал. Если нам говорят использовать uintptr_t вместо size_t, тогда почему для этого требуется reinterpret_cast? Кажется, что подойдет простой static_cast, поскольку стандарт специально предоставляет совместимые типы данных ...

jww 27.09.2016 19:19

@jww read: en.cppreference.com/w/cpp/language/static_cast Насколько я понимаю, static_cast может преобразовать тип или, если это указатель, может выполнять корректировку указателя, если типу это нужно. reinterpret_cast на самом деле просто меняет тип базового паттерна памяти (без мутаций). Чтобы уточнить: static_cast здесь ведет себя идентично.

Alexander Oh 10.11.2016 18:15

вместо этого он должен быть отмечен как выбранный ответ, поскольку он предоставляет все подробности того, как преобразовать в C и C++.

HaseeB Mir 26.04.2018 17:45

Итак, есть люди, пишущие на C++, у которых по имени переменной стоит *! Прекрасный. Я съеживаюсь каждый раз, когда вижу код вроде long* j; и даже хуже, когда он на C. Что касается приведений C в C++ (для указателей), я считаю, что более свежие версии GCC жалуются на это или, может быть, даже ограничивают его? А может это был эталон? В противном случае это мое воображение - что, я полагаю, вполне возможно.

Pryftan 18.11.2019 01:03

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

Если у вас нет uintptr_t, то uintmax_t тоже не является ответом: нет гарантии, что вы можете сохранить в нем значение указателя! Для этого может не быть целочисленного типа.

PierreBdR 18.03.2018 14:48

Другие вопросы по теме