Нужна помощь в понимании метода" 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
потому что 1
s слева отбрасываются и 0
s подаются справа. Затем повторное дополнение дает:
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.
наше число 75 мы хотим 4 бита, начиная с позиции 6. вызов функции будет getbits (75,6,4);
75 в двоичной 0100 1011
поэтому мы создаем маску длиной 4 бита, начиная с бита самого низкого порядка это делается как таковое.
~0 = 1111 1111
~ = 0000 1111
хорошо, мы получили нашу маску.
- теперь мы нажимаем биты, которые мы хотим из числа в биты самого низкого порядка, так что сдвинем двоичный код 75 на 6+1-4=3.
0100 1011 >>3 0000 1001
теперь у нас есть маска правильного количества битов в Нижнем порядке и битов, которые мы хотим из исходного числа в низком порядок.
- Итак, мы и они
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
×2
E2
представляется в типе результата, то это результирующее значение; в противном случае поведение не определено.
в 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 <<
либо