В чем разница между непосредственным присвоением результата операции левого сдвига переменной и операцией назначения левого сдвига в C?
В следующем выражении результат операции сдвига влево присваивается переменной i
,
int i;
i = 7 << 32;
printf("i = %d\n",i);
В следующем выражении выполняется операция присвоения левого сдвига.
int x = 7;
x <<= 32;
printf("x = %d\n",x);
Оба приведенных выше выражения дали разные результаты. Но это не то же самое со следующими двумя выражениями. Оба они дали одинаковый результат. Так что может быть причиной того, что вышеприведенные выражения возвращают разные значения?
int a;
a = 1 + 1;
printf("a = %d\n",a);
int b = 1;
b += 1;
printf("b = %d\n",b);
3 ответа
Стандарт C гласит:
Результат не определен, если правый операнд отрицательный или больше или равен числу битов в типе левого выражения.
Таким образом, это неопределенное поведение, потому что int
нормально 32
бит в размере, что означает, что только 0
через 31
шаги четко определены.
Я согласен с комментариями Коди Грея. Просто для людей в будущем, которые окажутся здесь, способ решить эту двусмысленность - использовать unsigned long long.
unsigned long long int b = 7ULL<<32; // ULL here is important, as it tells the compiler that the number being shifted is more than 32bit.
unsigned long long int a = 7;
a <<=32;
Абстрактная операционная семантика из ISO/IEC 9899
говорит:
6.5.7 Bitwise shift operators --- Semantics
3........ Если значение правого операнда отрицательно или больше или равно ширине повышенного левого операнда, поведение не определено.
В вашем случае, разобрав и увидев, что происходит, мы увидим так:
[root@arch stub]# objdump -d a.out | sed '/ <main>/,/^$/ !d'
00000000004004f6 <main>:
4004f6: 55 push %rbp
4004f7: 48 89 e5 mov %rsp,%rbp
4004fa: 48 83 ec 10 sub $0x10,%rsp
4004fe: c7 45 fc 07 00 00 00 movl $0x7,-0x4(%rbp)
400505: b8 20 00 00 00 mov $0x20,%eax
40050a: 89 c1 mov %eax,%ecx
40050c: d3 65 fc shll %cl,-0x4(%rbp) <<== HERE IS THE PROBLEM
40050f: 8b 45 fc mov -0x4(%rbp),%eax
400512: 89 c6 mov %eax,%esi
400514: bf b4 05 40 00 mov $0x4005b4,%edi
400519: b8 00 00 00 00 mov $0x0,%eax
40051e: e8 cd fe ff ff callq 4003f0 <printf@plt>
400523: b8 00 00 00 00 mov $0x0,%eax
400528: c9 leaveq
400529: c3 retq
40052a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
Сгенерированный код действительно пытается сдвинуться, но shll %cl,-0x4(%rbp)
(сдвиг влево от длинного) не имеет никакого эффекта.
undefined behaviour
в этом случае заключается в сборке, а именно в работе SHL.