В чем разница между прямым присвоением результата операции левого сдвига переменной и операции присваивания левого сдвига в 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);

Ответы

Ответ 1

В стандарте C говорится:

Результат undefined, если правый операнд отрицательный или больше чем или равно количеству бит в типе левых выражений.

Итак, это undefined поведение, потому что int обычно имеет размер 32, что означает, что только 0 через 31 шаги четко определены.

Ответ 2

Я согласен с комментариями Коди Грея. Просто для людей в будущем, которые здесь заканчиваются, способ разрешить эту двусмысленность - использовать 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;

Ответ 3

Абстрактная оперативная семантика из ISO/IEC 9899 гласит:

6.5.7 Bitwise shift operators --- Semantics

3........ Если значение правильного операнда отрицательный или больше или равен ширина продвинутого левого операнда, поведение undefined.

В вашем случае, разобрав и увидев, что происходит, мы видим так:

[[email protected] 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 <[email protected]>
  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.