2017-01-29 2 views
1

Я сделал небольшой тест на сдвиг бит в C, и все сдвиги на 0, 8, 16 бит в порядке, и я понял, что происходит.биты правого левого сдвига в C

Но 32-битный сдвиг вправо или влево, который не ясен для меня, переменная, с которой я выполняю тест, имеет длину 32 бит.

Затем я сменил 32-битные переменные, которые будут удерживать результаты сдвига, но 32-битные сдвиги вправо влево одинаковы!

Вот мой код:

#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <inttypes.h> 

int main() { 
    uint32_t code = 0xCDBAFFEE; 

    uint64_t bit32R = code >> 32; 
    uint16_t bit16R = code >> 16; 
    uint8_t bit8R = code >> 8; 
    uint8_t bit0R = code >> 0; 

    uint64_t bit32L = code << 32; 
    uint16_t bit16L = code << 16; 
    uint8_t bit8L = code << 8; 
    uint8_t bit0L = code << 0; 

    printf("Right shift:\nbit32R %.16x\nbit16R %x\nbit8R %x\nbit0R %x\n\n", 
      bit32R, bit16R, bit8R, bit0R); 
    printf("Left shift:\nbit32L %.16x\nbit16L %x\nbit8L %x\nbit0L %x\n\n", 
      bit32L, bit16L, bit8L, bit0L); 
} 

Вот результат я получаю:

Right shift: 
bit32R 00000000cdbaffee 
bit16R 0 
bit8R cdba 
bit0R ff 

Left shift: 
bit32L 00000000cdbaffee 
bit16L 0 
bit8L 0 
bit0L 0 

Process returned 61 (0x3D) execution time : 0.041 s 
Press any key to continue. 
+2

Ваш код вызывает неопределенное поведение. Значение сдвига '32' слишком велико для 32-битного' int' (то же самое для 16 бит, если ваша платформа имеет 16 бит 'int'). И используйте макросы 'PRInX' из' inttypes.h' для печати типов фиксированной ширины. У вас есть мнение о том, как работают целые операции. – Olaf

+0

Я сменил файл include на stdint! Что ничего не изменило, но я забыл о stdint и включил inttypes. Но я думаю, что они одинаковые, по крайней мере для моего кода. –

+0

Возможно, прежде чем делать предположения, полезно прочитать, что представляют заголовки, как они связаны и что означает «PRInX»? – Olaf

ответ

6

Правильное смещение целого числа на число бит, равное или большее, чем его размер, является неопределенным поведением.

С11 6.5.7 Битовые операторы сдвига

Синтаксис

shift-expression: additive-expression 
    shift-expression << additive-expression 
    shift-expression >> additive-expression 

Ограничения

Каждый из операндов должен иметь целочисленный тип.

Семантика

целочисленных акции выполняется на каждом из операндов. Тип результата - это продвинутый левый операнд. Если значение правого операнда отрицательное или больше или равно ширине продвинутого левого операнда, поведение не определено.

Результат E1 << E2 is E1 левый сдвиг E2 битовые позиции; освобожденные биты заполняются нулями. Если E1 имеет неподписанный тип, то значение результата будет E1 × 2 E2, приведенное по модулю больше, чем максимальное значение, представляемое в типе результата.Если E1 имеет знак и неотрицательное значение, а E1 × 2 E2 представляется в виде результата, то это результирующее значение; в противном случае поведение не определено.

Результат E1 >> E2: E1 Справа вправо E2 Битые позиции. Если E1 имеет неподписанный тип, или если E1 имеет подписанный тип и неотрицательное значение, значение результата является неотъемлемой частью отношения E1/2 E2. Если E1 имеет подписанный тип и отрицательное значение, результирующее значение определяется реализацией.

Размер int на вашей платформе, кажется, в большинстве 32 бит, так что инициализаторы для bit32R и bit32L имеют неопределенное поведение.

64-разрядные выражения должны быть записаны:

uint64_t bit32R = (uint64_t)code >> 32; 

и

uint64_t bit32L = (uint64_t)code << 32; 

Кроме того, форматы, используемые в printf не являются правильными для аргументов, переданных (если int не имеет 64 бита, которые будет производить различную продукцию).

Ваш компилятор, кажется, не полностью совместим с C99, вы должны добавить окончательный оператор return 0; в конце тела функции main().

Вот исправленный вариант:

#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <inttypes.h> 

int main(void) { 
    uint32_t code = 0xCDBAFFEE; 

    uint64_t bit32R = (uint64_t)code >> 32; 
    uint16_t bit16R = code >> 16; 
    uint8_t bit8R = code >> 8; 
    uint8_t bit0R = code >> 0; 

    uint64_t bit32L = (uint64_t)code << 32; 
    uint16_t bit16L = code << 16; 
    uint8_t bit8L = code << 8; 
    uint8_t bit0L = code << 0; 

    printf("Right shift:\n" 
      "bit32R %.16"PRIx64"\n" 
      "bit16R %"PRIx16"\n" 
      "bit8R %"PRIx8"\n" 
      "bit0R %"PRIx8"\n\n", 
      bit32R, bit16R, bit8R, bit0R); 

    printf("Left shift:\n" 
      "bit32L %.16"PRIx64"\n" 
      "bit16L %"PRIx16"\n" 
      "bit8L %"PRIx8"\n" 
      "bit0L %"PRIx8"\n\n", 
      bit32L, bit16L, bit8L, bit0L); 

    return 0; 
} 

Выход:

Right shift: 
bit32R 0000000000000000 
bit16R cdba 
bit8R ff 
bit0R ee 

Left shift: 
bit32L cdbaffee00000000 
bit16L 0 
bit8L 0 
bit0L ee 

это не может быть то, что вы ожидаете, потому что типы переменных несколько противоречивы.

+0

return 0 для main() в C99 никогда не был обязательным, говорится, что если опустить возвращаемое значение 0, ожидается, что оно будет предоставлено компилятором (прототип main() fuction также зависит от платформы, также является допустимым void main (void)). В то же время очень мало полностью совместимых с C99 компиляторов и даже меньше C++ 11 на данный момент, особенно на встроенных платформах. Я даже не был уверен, что он получил доступ к макросам PRIx, у компилятора, который у меня был у него, не было их в заголовке – Swift

+0

@Swift: статус, напечатанный рабочей средой 'Process received 61 (0x3D)' указывает, что компилятор сделал * * не ** добавить неявный 'return 0;', заданный C99, следовательно, мое утверждение о несоответствующем компиляторе. Я не удивлюсь, если эта среда основана на MSVC. В любом случае, хороший стиль всегда возвращает 0 (или какой-либо значимый статус выхода) явно в конце 'main()'. Семантика семантического возврата C99 - это хромое исправление для неаккуратного кода IMHO. – chqrlie

+0

Абсолютно! Это сработало просто отлично. Но почему (% .16x) не работал для 64-х переменных? Он показывает размер 64-битного гекса, но сдвиг неправильный. А с PRIx64 работает нормально? –

1

Одна проблема заключается в том, что вы используете %x для печати 64-разрядное целое число. Вы должны использовать правильный спецификатор формата для каждой переменной. Есть макросы для этого имеются:

#define __STDC_FORMAT_MACROS 
#include <inttypes.h> 

// ... 

printf("64 bit result: %" PRIx64 "\n", bit32R); 
printf("16 bit result: %" PRIx16 "\n", bit16R); 
printf("8 bit result: %" PRIx8 "\n", bit8R); 

Более подробную информацию можно найти here.

+0

Хотя '% x' ошибочно, использование PRIx64 не изменяет результат сдвига. –

+0

Если PRIx64 не изменит результат, то почему% x не так? Я получаю те же результаты с вашей модификацией. Я даже могу получить шестнадцатеричный полный формат 64-бит с модификатором% .16x. –

+0

@Perch - это специфические для платформы вещи. В целом, похоже, он работает, но затем либо сбой на других платформах, либо сбои при написании более продвинутого кода. Я признаю, что я не буду использовать все эти макросы, когда пишу код, но вы должны пропустить их, только если вы всегда знаете, на какой платформе вы будете работать. В противном случае произойдет бедствие. –

0

Вы не выполняете сдвиг влево в 64 бит, потому что код uint32_t, поэтому компилятор использует 32-битную версию оператора. Кроме того, вы должны сказать печати использовать длинные длинные (так же, как uint64_t)

#include <cstdint> 
#include <stdio.h> 
int main() 
{ 
uint32_t code = 0xCDBAFFEE; 


uint64_t bit32R=((uint64_t)code)>>32; 
uint16_t bit16R=code>>16; 
uint8_t bit8R=code>>8; 
uint8_t bit0R=code>>0; 

uint64_t bit32L=((uint64_t)code)<<32; 
uint16_t bit16L=code<<16; 
uint8_t bit8L=code<<8; 
uint8_t bit0L=code<<0; 


printf("Right shift:\nbit32R %llx\nbit16R %x\nbit8R %x\nbit0R %x\n\n", bit32R,bit16R,bit8R,bit0R); 
printf("Leftt shift:\nbit32L %llx\nbit16L %x\nbit8L %x\nbit0L %x", bit32L,bit16L,bit8L,bit0L); 
} 

Результат:

Right shift: 
bit32R 0 
bit16R cdba 
bit8R ff 
bit0R ee 

Leftt shift: 
bit32L cdbaffee00000000 
bit16L 0 
bit8L 0 
bit0L ee 

Вы должны использовать макросы, определенные в inttypes.h, если у вас есть совместимый компилятор C99, к сожалению, некоторые платформы не имеют таких определений. Дескрипторы формата для printf зависят от платформы.

+1

OP не претендует на выполнение 64 бит. Кроме того, 64 бит не является «длинным» на всех платформах. –

+0

@Olaf Dietsche: это в значительной степени было требованием. uint64_t бит32L = код << 32; такое же, как uint32_t бит32L = код << 32. Но я согласен, хотя на основании реакции вывода его printf платформа _his_ получила 64-битную длину. – Swift

+0

Но есть uint64_t, который дает вам 64-битную переменную. –

Смежные вопросы