Ошибка: приведение от '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

Как правильно разыграть это?

18 ответов

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

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

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

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

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

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

Вместо:

int x = (int)arg;

использовать:

int x = (long)arg;

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

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

Вместо использования длинного приведения вы должны привести к size_t.

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

Приведение указателя на 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* и обратно хорошо поддерживается (везде).

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

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

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

ids [i] = (uintptr_t) arg;

Тогда я могу продолжить компиляцию. Может быть, вы можете попробовать это тоже.

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

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

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

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

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

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

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

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

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

int myData = reinterpret_cast<int>(arg);

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

Редактировать:

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

Я хотел бы создать структуру и передать ее как 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() не использует его.

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

//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;

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

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

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

В моем случае я использовал 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);

То, что вы можете хотеть

int x = reinterpret_cast<int>(arg);

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

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

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

void thread_func(int arg){
...
}

и я создаю тему, как это:

pthread_create(&tid, NULL, (void*(*)(void*))thread_func, (void*)arg);
Другие вопросы по тегам