Как мне обработать «приведение из« void * »к« int »теряет точность» при компиляции 32-битного кода на 64-битной машине? - PullRequest
31 голосов
/ 08 января 2010

У меня есть пакет, который компилируется и отлично работает на 32-битной машине. Я сейчас пытаюсь заставить его скомпилировать на 64-битной машине и найти следующую ошибку-

 error: cast from ‘void*’ to ‘int’ loses precision

Есть ли флаг компилятора для подавления этих ошибок? или мне нужно вручную редактировать эти файлы, чтобы избежать этих бросков?

Ответы [ 12 ]

50 голосов
/ 08 января 2010

Проблема в том, что в 32 битах int (32-битное целое) будет содержать значение указателя.

Когда вы переходите на 64-битный, вы больше не можете хранить указатель в int - он недостаточно велик, чтобы содержать 64-битный указатель. Для этого предназначен тип intptr_t .

23 голосов
/ 08 января 2010

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

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

Исправьте ваш код. Или оставайтесь на 32-битной платформе, на которой в данный момент работает код.

Если ваш компилятор определяет intptr_t или uintptr_t, используйте их, поскольку они являются целочисленными типами, которые гарантированно будут достаточно большими для хранения указателя.

Если эти типы недоступны, size_t или ptrdiff_t также достаточно велики для размещения указателя на большинстве (не всех) платформах. Или используйте long (обычно это 64-битные на 64-битных платформах в компиляторе GCC) или long long (типы C99, которые большинство, но не все компиляторы, поддерживают в C ++), или какой-то другой определяемый реализацией интегральный тип шириной не менее 64 бит на 64-битной платформе.

13 голосов
/ 23 сентября 2010

Я предполагаю, что ситуация OP - это пустота *, используемая как общее хранилище для int, где пустота * больше, чем 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, преобразует адрес в указатель нужного типа данных, затем следует за указателем.

11 голосов
/ 08 января 2010

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

Даже если компилятор не выдаст ошибку, код, скорее всего, не будет работать. Код должен быть изменен и проверен на 64-битную совместимость.

7 голосов
/ 08 января 2010

Приведение указателя на int ужасно с точки зрения переносимости. Размер int определяется сочетанием компилятора и архитектуры. Вот почему был создан заголовок stdint.h, чтобы вы могли явно указать размер используемого вами типа на многих разных платформах с разными размерами слов.

Вам бы лучше перейти на uintptr_t или intptr_t (из stdint.h, и выберите тот, который лучше всего соответствует подписи, которая вам нужна).

4 голосов
/ 08 января 2010

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

Если вы подавите ошибки, это может даже работать некоторое время. Хотя указатель указывает на адрес в первых 4 ГБ, старшие 32 бита будут равны 0, и вы не потеряете данные. Но как только вы получите адрес> 4 ГБ, ваш код «загадочно» начнет работать.

Что вам нужно сделать, это изменить любой тип int, который может содержать указатель на intptr_t.

4 голосов
/ 08 января 2010

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

3 голосов
/ 08 января 2010

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

2 голосов
/ 10 января 2010

Как определено текущим стандартом C ++, нет целочисленного типа, который гарантированно будет содержать указатель. Некоторые платформы будут иметь intptr_t, но это не стандартная функция C ++. По сути, обработка битов указателя, как если бы они были целыми числами, не является переносимым делом (хотя это может быть сделано для работы на многих платформах).

Если причина приведения в том, чтобы сделать указатель непрозрачным, тогда void * уже достигнет этого, поэтому код мог бы использовать void * вместо int. Typedef может сделать это немного лучше в коде

typedef void * handle_t;

Если причина приведения состоит в том, чтобы сделать арифметику указателей с гранулярностью байтов, то, вероятно, лучший способ - привести к (char const *) и выполнить с этим математические вычисления.

Если причина приведения заключается в достижении совместимости с какой-либо существующей библиотекой (возможно, более старым интерфейсом обратного вызова), которую нельзя изменить, то я думаю, что вам необходимо просмотреть документацию для этой библиотеки. Если библиотека способна поддерживать необходимые вам функции (даже на 64-битной платформе), то в ее документации может быть указано предполагаемое решение.

2 голосов
/ 08 января 2010

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

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

...