Нужна помощь в понимании метода" getbits () " в главе 2 K&R C

в главе 2, раздел о побитовых операторах (раздел 2.9), у меня возникли проблемы с пониманием того, как работает один из методов выборки.

вот это метод:

unsigned int getbits(unsigned int x, int p, int n) {
    return (x >> (p + 1 - n)) & ~(~0 << n);
}

идея в том, что для данного числа x, он вернет n битов, начиная с позиции p, считая справа (самый дальний правый бит-позиция 0). Учитывая следующее main() метод:

int main(void) {
    int x = 0xF994, p = 4, n = 3;
    int z = getbits(x, p, n);
    printf("getbits(%u (%x), %d, %d) = %u (%X)n", x, x, p, n, z, z);

    return 0;
}

выход:

getbits(63892 (f994), 4, 3) = 5 (5)

я получаю части этого, но у меня проблемы с "большой картиной", в основном из-за бит (без каламбура), которые я не понимаю.

часть, с которой у меня конкретно возникают проблемы, - это дополнение:~(~0 << n). Я думаю, что я получаю первую часть, имея дело с x; это та часть (а затем маска), с которой я борюсь-и как все это приходит вместе, чтобы на самом деле получить эти биты. (Что я проверил, что он делает, как с кодом, так и с проверкой моих результатов с помощью calc.exe -- слава Богу, у него есть двоичный вид!)

помочь?

6 ответов


давайте использовать 16 бит для нашего примера. В этом случае ~0 равно

1111111111111111

когда мы ушли-сдвиньте это n биты (3 в вашем случае), мы получаем:

1111111111111000

потому что 1s слева отбрасываются и 0s подаются справа. Затем повторное дополнение дает:

0000000000000111

так что это просто умный способ получить n 1-бит в наименее значительной части числа.

" X бит", который вы описываете, сдвинул данный число (f994) достаточно далеко, чтобы наименее значимые 3 бита были теми, которые вы хотите. В этом примере биты, которые вы запрашиваете, окружены '.' письмена.

ff94             11111111100.101.00  # original number
>> p+1-n     [2] 0011111111100.101.  # shift desired bits to right
& ~(~0 << n) [7] 0000000000000.101.  # clear all the other (left) bits

и там у вас есть свои биты. Та да !!


Я бы сказал, что лучше всего сделать это сделать проблему вручную, таким образом, вы поймете, как это работает.

вот что я сделал, используя 8-битный unsigned int.

  1. наше число 75 мы хотим 4 бита, начиная с позиции 6. вызов функции будет getbits (75,6,4);

  2. 75 в двоичной 0100 1011

  3. поэтому мы создаем маску длиной 4 бита, начиная с бита самого низкого порядка это делается как таковое.

~0 = 1111 1111
~ = 0000 1111

хорошо, мы получили нашу маску.

  1. теперь мы нажимаем биты, которые мы хотим из числа в биты самого низкого порядка, так что сдвинем двоичный код 75 на 6+1-4=3.

0100 1011 >>3 0000 1001

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

  1. Итак, мы и они
  0000 1001 
& 0000 1111 ============ 0000 1001

таким образом, ответ десятичный 9.

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


~(~0 << n) создает маску, которая будет иметь n правый бит включен.

0
   0000000000000000
~0
   1111111111111111
~0 << 4
   1111111111110000
~(~0 << 4)
   0000000000001111

ANDing результат с чем-то еще вернет то, что в этих n бит.

Edit: я хотел указать на калькулятор этого программиста, который я использую навсегда:AnalogX PCalc.


никто еще не упоминал об этом, но в ANSI C ~0 << n вызывает неопределенное поведение.

это так ~0 является отрицательным числом, а смещение влево отрицательных чисел не определено.

ссылка: C11 6.5.7 / 4 (более ранние версии имели аналогичный текст)

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

в K&R C этот код полагался бы на конкретный класс системы, которую K&R разработал, наивно сдвигая 1 биты слева при выполнении сдвига влево подписанного числа (и этот код также полагается на представление дополнения 2), но некоторые другие системы не разделяют эти свойства, поэтому стандартизация C процесс не определял это поведение.

так что этот пример действительно интересен только как историческое любопытство, он не должен использоваться ни в каком реальном коде с 1989 года (если не раньше).


на примере: int x = 0xF994, p = 4, n = 3; int z = getbits (x, p, n);

и фокусировка на этом наборе операций ~(~0

для любого набора битов (10010011 и т. д.) Вы хотите создать "маску", которая тянет только биты, которые вы хотите видеть. Итак, 10010011 или 0x03, меня интересует xxxxx011. Что такое маска, которая извлечет этот набор ? 00000111 теперь я хочу быть независимым от sizeof int, я позволю машине выполнить работу, т. е. начать с 0 для байтовой машины это 0x00 для машины word, это 0x0000 и т. д. 64-разрядная машина будет представлять 64 бита или 0x0000000000000000

Теперь примените "не" (~0) и получите 11111111
сдвиньте вправо ( и "не", что и получить 00000111

так 10010011 & 00000111 = 00000011
вы помните, как работают логические операции ?


на ANSI C ~0 >> n вызывает неопределенное поведение

/ / сообщение о смещении влево, вызывающем проблему, неверно.

неподписанный символ m, l;

m = ~0 > > 4; производит 255 и его равно ~0, но,

m = ~0; l = m >> 4; производит правильное значение 15 такое же, как:

m = 255 > > 4;

нет проблем с левым сдвигом отрицательный ~0 << либо