Как правильно выполнить байтовый сдвиг блока данных?

у меня есть ммап

void *mymap;
mymap = mmap(0, attr.st_size, PROT_READ|PROT_WRITE, MAPFILE|MAP_SHARED, fd, 0);

Я открыл файл с содержимым HEX 0x25362364, который

00100101001101100010001101100100

в двоичном формате. Теперь я хочу выполнить битовый сдвиг:

char *str = (char *)mymap;
for(int i=0;i<attr.st_size;i++) {
    str[i] = str[i] >> 4;
}

мой новый файл содержит новое двоичное число

00000010000000110000001000000110

но желаемый результат состоял в том, чтобы сдвинуть все на 4 бита вправо:

00000010010100110110001000110110

как я могу это сделать? бонусный вопрос: если двоичные числа MSB равны 1, как я могу заполнить левую сторону нулями при сдвиге вправо?


person Daniel Gretzke    schedule 22.11.2017    source источник
comment
Мы можем начать с того, почему ваш подход не работает. Символ имеет ширину 8 бит. Когда вы сдвигаете биты каждого символа, вы отбрасываете биты, которые вы сдвигали из каждого отдельного чата; он не переходит к следующему символу в массиве. Теперь о решении, которое может зависеть от следующего вопроса: будет ли количество байтов, с которыми вы работаете, постоянным или оно может меняться?   -  person Christian Gibbons    schedule 22.11.2017
comment
О, и если вы хотите убедиться, что 0 смещаются, когда MSB равен единице, я считаю, что вы должны смещаться на беззнаковый тип.   -  person Christian Gibbons    schedule 22.11.2017
comment
количество байтов будет меняться. Спасибо за ответ   -  person Daniel Gretzke    schedule 23.11.2017
comment
Используйте решение Weather Vane (и отметьте его принятым). Это лучший способ сделать это для вашего варианта использования.   -  person Christian Gibbons    schedule 23.11.2017
comment
насколько я понял, он работает только до 8 бит. что, если я хочу сдвинуть пару сотен бит?   -  person Daniel Gretzke    schedule 23.11.2017


Ответы (1)


Каждый байт в массиве должен быть сдвинут вправо на 4 бита, а предыдущий (беззнаковый) байт должен быть сдвинут вправо на 4 бита. Например

unsigned char *str = (unsigned char *)mymap;
unsigned char prev = 0, next;
for(int i = 0; i < attr.st_size; i++) {
    next  = str[i];
    str[i] = (str[i] >> 4) | (prev << 4);
    prev = next;    
}

В случае, когда вам нужен сдвиг вправо на 5 бит, вы должны сдвинуться вправо на 5 бит и влево на 3 бита, сумма = 8 (при условии, что CHAR_BIT равно 8).

person Weather Vane    schedule 22.11.2017
comment
спасибо за вашу помощь, это работает. Но что мне делать, если я хочу сдвинуть более 8 бит, даже до пары сотен бит (если это файл большего размера) - person Daniel Gretzke; 23.11.2017
comment
Затем используйте массив unsigned int или unsigned long long и соответствующим образом измените алгоритм. Однако вам, возможно, придется остерегаться порядка байтов. Этот подход не будет работать для 100 бит, но это другой вопрос. Хм, у вас есть 128-битный тип данных? - person Weather Vane; 23.11.2017
comment
Я предлагаю сдвинуть большое количество бит, чтобы построить другой массив с соответствующими смещениями индексов и сдвигами. - person Weather Vane; 23.11.2017
comment
Если вы хотите сдвинуться на 100 бит, вы можете сдвинуться на 12 байт (используя memmove или подобное), а затем на 4 бита. - person user253751; 23.11.2017
comment
Что сказал @immibis. По сути, у вас будет грубый сдвиг, который перемещает целые байты, а затем закончите точным сдвигом, который уже предоставил WeatherVane. - person Christian Gibbons; 23.11.2017