Является ли этот битовый код оператора побочными эффектами (термин, использованный в книге K & R C) или машинно-зависимой инструкцией обработки? - PullRequest
3 голосов
/ 24 мая 2019

Вот два кода, которые, кажется, делают то же самое, но это не так.Эти два отличия при запуске и сравнение вывода с трассировкой приводят в замешательство, поскольку кажется, что первая обработка кода является машинно-зависимым кодом.Пожалуйста, прочитайте два кода

Код 1: -

unsigned char c=(((~0 << 3) >> 4) << 1);
printf("%d", c);

Выход: - 254

Код 2: -

unsigned char c=(~0 << 3);
c >>= 4;
c <<= 1;
printf("%d", c);

Выход:-.30

Вывод вышеприведенного кода отличается.

Не только этот код (1-й код) приводит к путанице, но и все типы кода, включающие однострочный оператор многократного побитового сдвига, дают неожиданные результаты.

2-й код работает правильно.

Пожалуйста, запустите этот код на своем компьютере и проверьте вышеприведенный вывод

И / ИЛИ

Объясните, почему эти выходные данные не совпадают.

ИЛИ

Наконец, мы должны понять, что мы не должны применять оператор многократного побитового сдвига в нашем коде.

Спасибо

Ответы [ 5 ]

10 голосов
/ 24 мая 2019

~0 << 3 всегда ошибка, ни один пример не верен.

  • 0 имеет тип int, который подписан.
  • ~0 преобразует двоичное содержимое во все: 0xFF...FF.
  • Когда вы оставляете данные сдвига в знаковом бите целого числа со знаком, вы вызываете неопределенное поведение. То же самое, если вы оставили смещение отрицательного целого числа.

Вывод: ни один из примеров не имеет детерминированного вывода, и оба могут аварийно завершить работу или вывести мусор.

3 голосов
/ 24 мая 2019

Во-первых, ~0 << 3 вызывает неопределенное поведение , потому что ~0 - это целочисленное значение со знаком со всеми битами, установленными в 1, и вы впоследствии оставили сдвиг в знаковый бит.

Изменение этого значения на ~0u << 3 предотвращает UB, но выводит тот же результат, поэтому возникает вопрос, почему.

Итак, сначала мы имеем это:

~0u

Который имеет тип unsigned int. Это не менее 16 бит, поэтому значение равно:

0xffff

Тогда это:

`~0u << 3`

Дает вам:

0xfff8

Тогда это:

((~0 << 3) >> 4)

Дает вам:

0x0fff

А это:

(((~0 << 3) >> 4) << 1)

Дает вам:

0x1ffe

Назначение этого значения unsigned char эффективно обрезает его до младшего байта:

0xfe

Так печатается 254.

Теперь во втором случае вы начинаете с этого:

unsigned char c = (~0 << 3);

Сверху это назначает 0xfff8 на c, который усекается до 0xf8. Тогда >> 4 дает вам 0x0f, а << 1 дает вам 0x1e, что составляет 30.

0 голосов
/ 25 мая 2019

Спасибо Томасу Джагеру за ответ (заданный в комментарии к вопросу)

Решение простое.

В 1-м коде битовая манипуляция выполняется с использованием операнда в качестве знакового символа.Из-за этого двоичное число из двух дополнений продолжает изменять свою битовую комбинацию в процессе манипулирования битами.После этого результат двух дополнительных чисел преобразуется в положительное число перед присвоением переменной без знака c.Следовательно, в итоге получается 254.

Вопрос состоит в том, чтобы объяснить, почему два вывода кода отличаются.Мы все знаем, что код 2 работает хорошо.Поэтому я объясняю только, почему код 1 работает неправильно.

1-й код: -

unsigned char c=(((~0 << 3) >> 4) << 1);
printf("%d", c);

Трассировка 1-го кода выглядит следующим образом: -

Step 1:   ~0 -----> -1 ----(binary form)----> 11111111 with sign bit 1 (means negative)


Step 2:   (sign bit 1)11111111 << 3 -----shifting to left----> (sign bit 1)11111000


Step 3 ***:   (sign bit 1)11111000 >> 4 ----shifing to right-----> (sign bit 1)11111111

*[*** - The left most bits is 1 in Result because of sign extension 
     Sign bit 1 retain its bit to 1 but right shifting the number will append 1 to 
left most bits without modify sign bit to 0 . 
     Hence all left most bit append to 1 because sign bit 1 is supplying this 1 to 
left most bits while right shifting                                          ]*


Step 4:    (sign bit 1)11111111 << 1 ---shifting to left---> (sign bit 1)11111110


Step 5:    two complement number (sign bit 1)11111110 converted to positive number 
            by deleting only sign bit to 0.


Step 6:    Result : (sign bit 0)11111110 ---decimal equivalent---> 254

Iтолько объясните его ответ.

Спасибо всем, кто приложил усилия для ответа на этот вопрос.

0 голосов
/ 24 мая 2019

Я скомпилировал (с x86-64 gcc 9.1) эти две строки:

int main() {
    unsigned char e=(~0 << 1);

    unsigned char d=(((~0 << 3) >> 4) << 1);
}

И получил следующий вывод сборки:

main:
        push    rbp
        mov     rbp, rsp
        mov     BYTE PTR [rbp-1], -2
        mov     BYTE PTR [rbp-2], -2
        mov     eax, 0
        pop     rbp
        ret

Как видите, обе строки преобразованык той же инструкции mov BYTE PTR [rbp-1], -2.Похоже, что компилятор оптимизирует ваш первый код.

0 голосов
/ 24 мая 2019

В первом случае есть принуждение к символу только после окончания вычислений, в то время как во втором случае есть принуждение к символу после первого (~0 << 3).

Как я уже говорил в комментариях, по умолчанию применяются арифметические преобразования, которые влияют на принуждение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...