2015-01-04 4 views
3

Например, у меня есть следующий номер:Как включить бит в октабе, только если он равен 0?

0x FF 00 FF 

Теперь, я хочу, чтобы 00 часть, чтобы быть 1, только если это 0. Нужно ли мне проверять каждый байт или есть немного-трик, который может быть используется здесь?

Пример:

У меня есть неподписанный длинный IP, и я хэш его с функцией строки, но функция завершается на нулевых байтов, так что мне нужно, чтобы установить все нулевые байты 1:

0x7F000001 (127.0.0.1, может быть 0x0100007F, но зависит от платформы в любом случае)

теперь, я хочу сделать 0 байт в них:

0x7F010101 (так он будет 127.1.1.1)

Это единственный способ сделать то, что я хочу, это

char ip_sz[5]; 

*(unsigned long*)ip_sz = ulong_ip; 

if (!ip_sz[0]) ip_sz[0] = 1; 
if (!ip_sz[1]) ip_sz[1] = 1; 
if (!ip_sz[2]) ip_sz[2] = 1; 
if (!ip_sz[3]) ip_sz[3] = 1; 

?

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

+1

Не могли бы вы объяснить на примере, что именно вам нужно? – SMA

+0

Какой тип данных для этого? Мы говорим о битах 8-15 в интегральном типе? – Jon

+0

ИЛИ это с '11'? Если это 1, он останется 1. – Maroun

ответ

5

Вы можете проверить все байты равного нуля путем применения битовой маски (то есть число, содержащее те, в положениях, которые вы хотите проверить, и нули во всех остальных позициях *) к нему.

В вашем случае вам нужна битовая маска с восемью последовательными в позициях с нулевой позицией с 7 по 15 включительно, то есть 0x00FF00. Если вы примените побитовое «AND» & к маске и к числу n, результат будет равен нулю только тогда, когда все биты n в позициях с 7 по 15 являются нулями. В противном случае результатом будет некоторое ненулевое число.

int n = 0xFF00FF; 
int mask = 0x00FF00; 
if ((n & mask) == 0) { 
    ... // Modify n as needed 
} 

Вы можете построить восемь-битную битовую маску вручную, или использовать левый оператор сдвига, как это:

(0xFF)  // 0x000000FF 
(0xFF << 8) // 0x0000FF00 
(0xFF << 16) // 0x00FF0000 
(0xFF << 24) // 0xFF000000 

так, я бы до сих пор сделать четыре if «S?

В данном конкретном случае if s не нужны, из-за небольшой трюк в C: вы можете использовать ! для преобразования ноль в одно и любое другое число к нулю. Я проиллюстрирую это одним байтом; вы можете использовать битовые сдвиги, чтобы развернуть это число до целых чисел.

uint8_t n = ... // Some value 
n = n | (!n); // Here is the trick 

Последняя операция преобразует 0x00 до 0x01, в то время как все другие значения (т.е. 0x01 до 0xFF включительно) остаются нетронутыми (demo).

* Конструкция битовой маски зависит от действия, которое вы применяете к ней. Для «И» и «XOR» вы отмечаете нужные биты с единицами, тогда как для «ИЛИ» вы отмечаете их нулями.

+0

Итак, мне еще нужно сделать четыре 'if''s – Gizmo

+0

@Gizmo Не обязательно (посмотрите на редактирование). – dasblinkenlight

+0

Я посмотрел на него, функция 'flip' с моим clang компилируется в последовательность« testb, sete; orb »из инструкций Intel. Дело в том, что все еще нужно отделить четыре октета, сделать это с каждым из них и построить полученное 32-битное значение. –

1

Я googled "обнаружить нулевой байт в слове". Я советую вам поступать так же. Этот элемент не имеет ветвей, использует пять операций (по крайней мере, в C, число машинных кодовых инструкций зависит от вашего компилятора и платформы, а что - нет). Я думаю, вы не выиграть много с этим, но для удовольствия, вот это:

#include <stdio.h> 
#include <stdint.h> 
#include <inttypes.h> 

static void print(uint32_t x) 
{ 
    printf("0x%08" PRIX32 "\n", x); 
} 

int main() 
{ 
    uint32_t x; 

    x = 0xb300ff00; /* <- an example, should work with any 32 bit value */ 
    print(x); 
    print(x - 0x01010101); 
    print((x - 0x01010101) & ~x); 
    print((x - 0x01010101) & ~x & 0x80808080); 
    print(((x - 0x01010101) & ~x & 0x80808080) >> 7); 
    print(x | ((x - 0x01010101) & ~x & 0x80808080) >> 7); /* <- the final result */ 

    return 0; 
} 

EDIT:

Как примечание стороны, это генерируется компилятором:

leal -16843009(%rdi), %eax 
movl %edi, %ecx 
notl %ecx 
andl %ecx, %eax 
shrl $7, %eax 
andl $16843009, %eax 
orl %edi, %eax 

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