Как я должен обрабатывать "cast from" void * to "int loses precision" при компиляции 32-битного кода на 64-битной машине?
У меня есть пакет, который компилируется и отлично работает на 32-битной машине. Теперь я пытаюсь собрать его на 64-битной машине и найти следующую ошибку -
error: cast from ‘void*’ to ‘int’ loses precision
Есть ли флаг компилятора для подавления этих ошибок? или мне нужно вручную редактировать эти файлы, чтобы избежать этих приведений?
Ответы
Ответ 1
Проблема заключается в том, что в 32 битах int (который является 32-битным целым числом) будет содержать значение указателя.
Когда вы переходите на 64-битный, вы больше не можете хранить указатель в int - он недостаточно велик для хранения 64-битного указателя. Для этого предназначен тип intptr_t.
Ответ 2
Ваш код не работает. Он не станет менее разрушенным, игнорируя предупреждения, предоставленные компилятором.
Как вы думаете, что произойдет, когда вы попытаетесь сохранить указатель шириной 64 бит в 32-битное целое число? Половина ваших данных будет выброшена. Я не могу представить много случаев, когда это правильная вещь, или где она не вызовет ошибок.
Исправьте свой код. Или оставайтесь на 32-битной платформе, на которой работает данный код.
Если ваш компилятор определяет intptr_t
или uintptr_t
, используйте их, поскольку они являются целыми типами, которые гарантируют, что они будут достаточно большими для хранения указателя.
Если эти типы недоступны, size_t
или ptrdiff_t
также достаточно велики, чтобы удерживать указатель на большинстве (не всех) платформ. Или используйте long
(обычно это 64-разрядные на 64-разрядных платформах в компиляторе GCC) или long long
(типы C99, которые большинство, но не все компиляторы, поддерживают на С++) или какой-либо другой определенный в реализации интегральный тип шириной не менее 64 бит на 64-битной платформе.
Ответ 3
Мое предположение - ситуация с ОП - это пустота * используется как общее хранилище для int, где void * больше, чем int. Итак, например:
int i = 123;
void *v = (void*)i; // 64bit void* being (ab)used to store 32bit value
[..]
int i2 = (int)v; // we want our 32bits of the 64bit void* back
Компилятор не любит эту последнюю строку.
Я не буду взвешивать, правильно ли или неправильно злоупотреблять пустотой * таким образом. Если вы действительно хотите обмануть компилятор, следующий метод работает, даже с -Wall:
int i2 = *((int*)&v);
Здесь он принимает адрес v, преобразует адрес в указатель нужного типа данных, затем следует указатель.
Ответ 4
Это ошибка по какой-либо причине: int
на вашей машине только наполовину меньше void*
, поэтому вы не можете просто сохранить void*
в int
. Вы потеряете половину указателя, и когда программа позже попытается вывести указатель из этого int
снова, он ничего не получит.
Даже если компилятор не выдаст ошибку, код, скорее всего, не сработает. Код должен быть изменен и проверен на совместимость с 64-битной версией.
Ответ 5
Листинг указателя на int ужасно с точки зрения переносимости. Размер int определяется сочетанием компилятора и архитектуры. Вот почему был создан заголовок stdint.h, чтобы вы могли явно указать размер используемого вами типа на разных платформах со многими различными размерами слов.
Вам лучше сбрасывать на uintptr_t или intptr_t (из stdint.h и выбирать тот, который лучше всего соответствует требуемой подписке).
Ответ 6
Вы можете попытаться использовать intptr_t
для лучшей переносимости вместо int, где требуются указания указателя, такие как обратные вызовы.
Ответ 7
Вы не хотите подавлять эти ошибки, потому что, скорее всего, они указывают на проблему с логикой кода.
Если вы подавляете ошибки, это может даже работать некоторое время. Хотя указатель указывает на адрес в первых 4 ГБ, верхние 32 бита будут равны 0, и вы не потеряете никаких данных. Но как только вы получите адреs > 4 ГБ, ваш код начнет "таинственно" не работать.
Что вам нужно сделать, это изменить любой int, который может содержать указатель на intptr_t.
Ответ 8
Вам нужно вручную отредактировать эти файлы, чтобы заменить их кодом, который вряд ли будет ошибочным и непереносимым.
Ответ 9
Подавление предупреждений - плохая идея, но может быть флаг компилятора для использования 64-битных ints, в зависимости от вашего компилятора и архитектуры, и это безопасный способ исправить проблему (предполагая, конечно, что код didn 't также предположить, что ints 32-бит). Для gcc флаг -m64.
Лучший ответ по-прежнему заключается в том, чтобы исправить код, я полагаю, но если это устаревший сторонний код и эти предупреждения бушуют, я не вижу, чтобы этот рефакторинг был очень эффективным использованием вашего времени. Определенно, не бросайте указатели на ints в любой из вашего нового кода.
Ответ 10
Как определено текущим стандартом С++, нет целочисленного типа, который гарантированно удерживает указатель. На некоторых платформах будет intptr_t, но это не стандартная функция С++. По сути, обработка битов указателя, как если бы они были целыми числами, не является переносимой задачей (хотя ее можно заставить работать на многих платформах).
Если причина приведения заключается в том, чтобы сделать указатель непрозрачным, то void * уже достигает этого, поэтому код может использовать void * вместо int. Typedef может сделать это немного лучше в коде
typedef void * handle_t;
Если причина для актера состоит в том, чтобы сделать арифметику указателя с гранулярностью байт, тогда лучший способ - это, вероятно, применить к (char const *) и выполнить математику с этим.
Если причина для трансляции заключается в достижении совместимости с какой-либо существующей библиотекой (возможно, более старым интерфейсом обратного вызова), которая не может быть изменена, я думаю, вам нужно просмотреть документацию для этой библиотеки. Если библиотека способна поддерживать требуемые функции (даже на 64-битной платформе), тогда ее документация может адресовать предполагаемое решение.
Ответ 11
У меня возникла аналогичная проблема. Я решил это следующим образом:
#ifdef 64BIT
typedef uint64_t tulong;
#else
typedef uint32_t tulong;
#endif
void * ptr = NULL; //Whatever you want to keep it.
int i;
i = (int)(tulong)ptr;
Я думаю, проблема заключается в приведении указателя в более короткий тип данных. Но для более крупного типа int
он отлично работает.
Я преобразовал эту проблему из typecasting указателя на long
в typecasting 64-битное целое число до 32-битного целого числа, и он работал нормально. Я все еще в поиске опции компилятора в GCC/Clang.
Ответ 12
Иногда бывает разумно разбить 64-битный элемент на 2 32-битных элемента. Вот как вы это сделаете:
Заголовочный файл:
//You only need this if you haven't got a definition of UInt32 from somewhere else
typedef unsigned int UInt32;
//x, when cast, points to the lower 32 bits
#define LO_32(x) (*( (UInt32 *) &x))
//address like an array to get to the higher bits (which are in position 1)
#define HI_32(x) (*( ( (UInt32 *) &x) + 1))
Исходный файл:
//Wherever your pointer points to
void *ptr = PTR_LOCATION
//32-bit UInt containing the upper bits
UInt32 upper_half = HI_32(ptr);
//32-bit UInt containing the lower bits
UInt32 lower_half = LO_32(ptr);