Подтвердить что ты не робот

Без знака минус беззнаковое различие между 32 и 64-битными

Ситуация:
У меня есть часть кода, которая работает, когда скомпилирована для 32-битных, но сбой при компиляции для 64-бит с gcc 4.6. После определения проблемы и чтения стандартов, я не могу понять, почему он работает для 32-бит. Надеюсь, кто-то может объяснить, что происходит.

Код (несколько упрощен и сокращен до интересных частей):

// tbl: unsigned short *, can be indexed with positive and negative values
// v: unsigned int
// p: unsigned char *
tmp = tbl[(v >> 8) - p[0]]; // Gives segfault when not compiled with -m32

При компиляции с -m32 работает код. При компиляции без -m32 он дает segfault. Причиной segfault является то, что (v >> 8) - p[0] интерпретируется как unsigned int при компиляции для 64-битного, что для "отрицательных" результатов будет отключено.

Согласно этому вопросу, в стандарте C99 говорится следующее:
6.2.5c9: A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

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

Может ли кто-нибудь объяснить, что происходит в 32-битном случае?

4b9b3361

Ответ 1

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

Чтобы выполнить арифметику указателя, компилятор продвигает индекс массива до той же ширины, что и указатель. Итак, для unsigned с 32-разрядными указателями вы получите тот же результат, что и int с 32-разрядными указателями.

Например,

char *p = (char *) 0x1000;

// always points to 0x0c00
// in 32-bit, the index is 0xfffffc00
// in 64-bit, the index is 0xfffffffffffffc00
int r = p[(int) -0x400]; 

// depends on architecture
// in 32-bit, the index is 0xfffffc00 (same as int)
// in 64-bit, the index is 0x00000000fffffc00 (different from int)
int r = p[(unsigned) -0x400];