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

Другие вопросы по тегам