Error: лить из 'void *' в 'int' теряет точность

У меня есть функция с прототипом void* myFcn(void* arg), которая используется в качестве отправной точки для pthread. Мне нужно преобразовать аргумент в int для последующего использования:

int x = (int)arg;

Компилятор (GCC версии 4.2.4) возвращает ошибку:

file.cpp:233: error: cast from 'void*' to 'int' loses precision

Каков правильный способ сделать это?

Ответ 1

Вы можете применить его к типу intptr_t. Это тип int гарантированно достаточно большой, чтобы содержать указатель. Используйте #include <cstdint>, чтобы определить его.

Ответ 2

Опять же, все ответы выше пропустили плохо. OP хотел преобразовать значение указателя в значение int, вместо этого большинство ответов, так или иначе, пытались ошибочно преобразовать содержимое аргументов arg в значение int. И большинство из них даже не будут работать на gcc4.

Правильный ответ: если вы не против потери точности данных,

int x = *((int*)(&arg));

Это работает на GCC4.

Лучше всего, если можно, не делайте такого литья, вместо этого, если один и тот же адрес памяти должен быть общим для указателя и int (например, для сохранения ОЗУ), используйте union и убедитесь, что если mem адрес рассматривается как int, только если вы знаете, что он был последним установлен как int.

Ответ 3

В общем случае нет подходящего способа сделать это для int. Стандартная библиотека C99 предоставляет intptr_t и uintptr_t typedefs, которые должны использоваться всякий раз, когда возникает необходимость выполнить такой отбор. Если ваша стандартная библиотека (даже если это не C99), оказывается, предоставляет эти типы - используйте их. Если нет, проверьте размер указателя на своей платформе, самостоятельно определите эти typedefs и используйте их.

Ответ 4

Вместо:

int x = (int)arg;

использование:

int x = (long)arg;

На большинстве платформ указатели и longs имеют одинаковый размер, но ints и указатели часто не равны по размеру на 64-битных платформах. Если вы конвертируете (void*) в (long), точность не теряется, то, назначив (long) на (int), он должным образом усекает число, которое должно соответствовать.

Ответ 5

Приведение указателя в void * и обратно является действительным использованием reinterpret_cast < > . Поэтому вы можете сделать это:

pthread_create(&thread, NULL, myFcn, new int(5)); // implicit cast to void* from int*

Затем в myFcn:

void* myFcn(void* arg)
{
    int*  data = reinterpret_cast<int*>(arg);
    int   x    = *data;
    delete data;

Примечание. Как указывает sbi, для создания потока потребуется изменение в вызове OP.

То, что я пытаюсь подчеркнуть, что преобразование из int в указатель и обратно снова может быть связано с проблемами при переходе с платформы на платформу. НО преобразование указателя на void * и обратно снова хорошо поддерживается (везде).

Таким образом, в результате может быть меньше склонности к ошибкам генерировать указатель динамически и использовать его. Помните о необходимости удалить указатель после использования, чтобы мы не просочились.

Ответ 6

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

int val = (int) ((size_t) arg);

Ответ 7

Правильный способ - передать его другому pointer type. Преобразование a void* в int является не переносным способом, который может работать или не может быть! Если вам нужно сохранить возвращенный адрес, просто сохраните его как void*.

Ответ 8

Я тоже встречаюсь с этой проблемой.

ids [i] = (int) arg;//здесь возникает ошибка = > Я изменяю это ниже.

ids [i] = (uintptr_t) arg;

Затем я могу продолжить компиляцию. Возможно, вы тоже можете попробовать.

Ответ 9

Если вы вызываете функцию создания потока, подобную этой

pthread_create(&thread, NULL, myFcn, reinterpret_cast<void*>(5));

то void*, входящий внутрь myFcn, имеет значение int, которое вы вложили в него. Итак, вы знаете, что можете отбросить это назад.

int myData = reinterpret_cast<int>(arg);

даже несмотря на то, что компилятор не знает, что вы только передаете myFcn в pthread_create в сочетании с целым числом.

Edit:

Как указывал Мартин, это предполагает, что sizeof(void*)>=sizeof(int). Если ваш код имеет возможность портироваться на какую-либо платформу, где это не выполняется, это не сработает.

Ответ 10

Самый безопасный способ:

static_cast<int>(reinterpret_cast<long>(void * your_variable));

long гарантирует размер указателя на Linux на любом компьютере. Windows имеет 32-битную длину только на 64 бит. Поэтому вам нужно изменить его на long long вместо long в Windows на 64 бит. Таким образом, reinterpret_cast присвоил его типу long, а затем static_cast безопасно отбрасывает long в int, если вы готовы сделать truncte данных.

Ответ 11

Я бы создал структуру и передал ее как void * в pthread_create

struct threadArg {
    int intData;
    long longData;
    etc...
};


threadArg thrArg;
thrArg.intData = 4;
...
pthread_create(&thread, NULL, myFcn, (void*)(threadArg*)&thrArg);


void* myFcn(void* arg)
{
    threadArg* pThrArg = (threadArg*)arg;
    int computeSomething = pThrArg->intData;
    ...
}

Имейте в виду, что thrArg должен существовать до тех пор, пока myFcn() не использует его.

Ответ 12

Нет никакого "правильного" способа хранения 64-разрядного указателя в 32-разрядном целое. Проблема заключается не в кастинге, а в том, что целевой тип теряет половину указателя. 32 оставшихся бита, хранящихся внутри int, недостаточны для восстановления указателя на функцию потока. Большинство ответов просто пытаются извлечь 32 бесполезных бита из аргумента.

Как Ferruccio, int должен быть заменен на intptr_t, чтобы сделать программу значимой.

Ответ 13

Не передавайте свой int как void*, передайте int* на ваш int, поэтому вы можете наложить void* на int* и скопировать указатель разыменования на ваш int.

int x = *static_cast<int*>(arg);

Ответ 14

Что вы можете пожелать, это

int x = reinterpret_cast<int>(arg);

Это позволяет вам переосмыслить void * как int.

Ответ 15

//new_fd is a int
pthread_create(&threads[threads_in_use] , NULL, accept_request, (void*)((long)new_fd));

//inside the method I then have
int client;
client = (long)new_fd;

Надеюсь, что это поможет

Ответ 16

В моем случае я использовал 32-битное значение, которое нужно было передать функции OpenGL в виде void *, представляющего смещение в буфер.

Вы не можете просто перевести 32-битную переменную в указатель, потому что этот указатель на 64-битной машине в два раза длиннее. Половина вашего указателя будет мусором. Вам нужно передать фактический указатель.

Это даст вам указатель на 32-битное смещение:

int32 nOffset   = 762;       // random offset
char * pOffset  = NULL;      // pointer to hold offset
pOffset         += nOffset;  // this will now hold the value of 762 correctly
glVertexAttribPointer(binding, nStep, glType, glTrueFalse, VertSize(), pOffset);

Ответ 17

Указатель функции несовместим с void * (и любым другим указателем нефункции)

Ответ 18

Ну, это происходит потому, что вы конвертируете 64-битный указатель в 32-битное целое число, чтобы потерять информацию.

Вы можете использовать целое число из 64 бит вместо howerver. Обычно я использую функцию с правильным прототипом, и я использую тип функции: например.

void thread_func(int arg){
...
}

и я создаю поток следующим образом:

pthread_create(&tid, NULL, (void*(*)(void*))thread_func, (void*)arg);