Неожиданное расширение знака int32 или 32-битного указателя при преобразовании в uint64 - PullRequest
3 голосов
/ 06 октября 2011

Я скомпилировал этот код, используя Visual Studio 2010 (cl.exe /W4) как файл C:

int main( int argc, char *argv[] )
{
    unsigned __int64 a = 0x00000000FFFFFFFF;
    void *orig = (void *)0xFFFFFFFF;
    unsigned __int64 b = (unsigned __int64)orig;
    if( a != b )
        printf( " problem\ta: %016I64X\tb: %016I64X\n", a, b );
    return;
}

Предупреждений нет, и в результате получается:

проблема a: 00000000FFFFFFFF b: FFFFFFFFFFFFFFFF

Полагаю, int orig = (int)0xFFFFFFFF будет менее спорным, поскольку я не назначаю указатель на целое число.Однако результат будет таким же.

Может ли кто-нибудь объяснить мне, где в стандарте C рассматривается, что orig - это знак, расширенный от 0xFFFFFFFF до 0xFFFFFFFFFFFFFFFF?

Я предположил, что (unsigned __int64)orig станет 0x00000000FFFFFFFF.Похоже, что сначала выполняется преобразование в тип со знаком __int64, а затем оно становится без знака?

РЕДАКТИРОВАТЬ: на этот вопрос дан ответ в том, что указатели имеют расширенный знак, поэтому я вижу такое поведениев gcc и msvc.Однако я не понимаю, почему, когда я делаю что-то вроде (unsigned __int64)(int)0xF0000000, его знак расширяется до 0xFFFFFFFFF0000000, но (unsigned __int64)0xF0000000 вместо этого не показывает то, что я хочу, а именно 0x00000000F0000000.

РЕДАКТИРОВАТЬ: ответ на вышеуказанное редактирование.Причина, по которой (unsigned __int64)(int)0xF0000000 является расширением знака, заключается в том, что, как отметил пользователь R :

Преобразование типа со знаком (или любого типа) в тип без знака всегда происходит через уменьшение по модулю один плюс максимальное значение целевого типа.

И в (unsigned __int64)0xF0000000 0xF0000000 начинается как целочисленный тип без знака, потому что он не может поместиться в целочисленный тип.Затем этот уже беззнаковый тип преобразуется unsigned __int64.

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

unsigned __int64 functionidontcontrol( char * );
unsigned __int64 x;
void *y = thisisa32bitaddress;
x = functionidontcontrol(str);
if( x != (uintptr_t)y )



Снова отредактируйте: вот что я нашел в стандарте C99: 6.3.1.3 Целые числа со знаком и без знака

  • 1 Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.
  • 2 В противном случае,если новый тип является беззнаковым, значение преобразуется путем многократного сложения или вычитания значения, превышающего максимальное значение, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне нового типа.49)
  • 3 В противном случае новый тип подписывается и значение не может быть представлено в нем;либо результат определяется реализацией, либо генерируется определяемый реализацией сигнал.
  • 49) Правила описывают арифметику математического значения, а не значения выражения данного типа.

Ответы [ 4 ]

6 голосов
/ 06 октября 2011

Преобразование указателя в / из целого числа определяется реализацией.

Здесь - это то, как это делает gcc, то есть знак расширяется, если целочисленный тип больше, чем тип указателя (этоЭто произойдет независимо от того, является ли целое число подписанным или неподписанным, просто потому, что именно так gcc решил его реализовать).

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

0 голосов
/ 06 октября 2011

Используйте это, чтобы избежать расширения знака:

unsigned __int64 a = 0x00000000FFFFFFFFLL;

Обратите внимание на букву L в конце. Без этого он интерпретируется как 32-разрядное число со знаком (-1) и затем приводится.

0 голосов
/ 06 октября 2011

Целочисленные константы (например, 0x00000000FFFFFFFF) по умолчанию являются целыми числами со знаком и, следовательно, могут иметь расширение знака при назначении 64-битной переменной.Попробуйте заменить значение в строке 3 на:

0x00000000FFFFFFFFULL
0 голосов
/ 06 октября 2011

Из стандарта C99 (§6.3.2.3 / 6):

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

Так что вам нужно найти документацию вашего компилятора, в которой говорится об этом.

...