Сдвиг на N бит всего массива символов

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

Пример: kxmo смещенный на 3 бита влево должен стать X@hx

Это то, что у меня есть в настоящее время, но оно работает не так, как ожидалось:

#include <stdio.h>

int main(void) {
    //shift the array with length *len* *shift* bits to the left
    int len = 4, shift = 3;
    unsigned char a[len] = "kxmo";
    unsigned char b[len]; //X@hx

    unsigned char tmp = 0, tmp2 = 0;
    for(int i = len - 1; i > 0; i--) {
        tmp = 0 | (a[i] << shift);
        b[i] = a[i];

        tmp2 = 0 | (a[i - 1] << shift);
        b[i - 1] = (a[i - 1] << shift) ^ tmp;
    }

    printf("old: %s | new: %s\n", a, b);

    return 0;
}

Где я терплю поражение?

Редактировать:

Вот что я получаю прямо сейчас: old: kxmo | new: �xmo


person alexandernst    schedule 05.09.2015    source источник
comment
В чем вопрос? Что вы получаете в отладчике? Что вы пробовали самостоятельно?   -  person too honest for this site    schedule 05.09.2015
comment
@Olaf Я получаю �xmo, чего я не ожидал. И я попробовал код, который написал в своем вопросе.   -  person alexandernst    schedule 05.09.2015
comment
0 | whatever совпадает с whatever. Как вы рассчитали результат пробы? Ваш код делает то же самое? Подсказка: XOR здесь бесполезен.   -  person too honest for this site    schedule 05.09.2015
comment
@alk Мой вопрос уже содержит то, что я ожидаю от вывода. Это второе предложение. Добавлю то, что сейчас получаю.   -  person alexandernst    schedule 05.09.2015
comment
Достаточно честно, извините. У меня почему-то наблюдается аллергическая реакция на фразу не работает:}   -  person alk    schedule 05.09.2015
comment
Предлагаю вам нарисовать картинку.   -  person alk    schedule 05.09.2015
comment
Почему вы начинаете с правой стороны, когда пытаетесь сместиться влево?   -  person alk    schedule 05.09.2015
comment
@alk Я не уверен, что есть разница, начиная слева или справа. В любом случае мне придется перенести дополнительные биты в следующий байт.   -  person alexandernst    schedule 05.09.2015
comment
Верный. Однако, если вы начинаете слева, вам не понадобится второй массив. Размышляя над этой проблемой, я представил себя и дюжину пивных ящиков, которые видят, что мне нужно сдвинуть все бутылки на определенное количество бутылок влево ... ;-) Я подошел к этому слева.   -  person alk    schedule 05.09.2015


Ответы (2)


Сначала представьте, что вы делаете это карандашом и бумагой. Допустим, вы сдвигаете два байта на три бита, вы начинаете с байтов abcdefgh, ijklmnop и хотите закончить с defghijk, lmnop000.

Для этого вам нужно извлечь 00000ijk из второго байта и OR его в первый байт после сдвига. Для этого вам нужно сдвинуть второй байт на 8-shift вправо вправо и замаскировать результат с помощью 00000111, то есть последних shift битов, установленных на 1. Эта маска может быть построена путем сдвига 1 влево shift+1 раз, получения 00001000 и вычитания 1 из результата.

Вот как это сделать:

char b1 = 'k';
char b2 = 'x';
int shift = 3;
int carry = 0, nextCarry;

nextCarry = (b1 >> (8-shift)) & ((1<<(shift+1))-1);
b1 <<= shift;
b1 |= carry;
carry = nextCarry;

Теперь сделайте то же самое с b2:

nextCarry = (b2 >> (8-shift)) & ((1<<(shift+1))-1);
b2 <<= shift;
b2 |= carry;
carry = nextCarry;

Если вы будете делать это в цикле, вы добьетесь желаемого результата.

Демо.

person Sergey Kalinichenko    schedule 05.09.2015
comment
Я тестирую ваш код, но результат оказался не таким, как я ожидал. Это результат printf("%s", b); - ›[�kx. Последний символ в порядке, но с этого момента все остальные символы неверны. - person alexandernst; 05.09.2015
comment
@alexandernst Вы ожидаете неверного результата. Если вы вручную сдвинете "kxmo", то есть {6B 78 6D 6F}, влево на три бита, вы получите {5B C3 6B 78}, то есть [�kx. - person Sergey Kalinichenko; 05.09.2015
comment
Действительно, мой ожидаемый результат был неправильным, так как @AlexLop. сказал. Ваш код выводит то, что должен выводить. Спасибо. - person alexandernst; 05.09.2015

Как насчет этого (при условии, что 0 ‹= shift‹ 8):

#define BITS_IN_BYTE 8
for(int i = len - 1; i > 0; i--) 
{
    b[i] = a[i] << shift;
    b[i - 1] = (a[i - 1] << shift) | (a[i] >> (BITS_IN_BYTE - shift));
}

Я не проверял, но надеюсь, что он будет делать то, что вы хотите.

ИЗМЕНИТЬ

Хорошо, я проверил, и он делает то, что вы ожидаете.

ПРИМЕЧАНИЕ -> вам нужно установить len на 5, а не на 4 для '\0'. также обратите внимание, что первая итерация (b[i] = a[i] << shift;) будет выполнена на '\0', но, поскольку его значение равно 0, все в порядке.

person Alex Lop.    schedule 05.09.2015
comment
Просто попробовал, второй символ вывода не @, что означает, что он работает не так, как ожидалось. - person alexandernst; 05.09.2015
comment
@alexandernst :) не может быть, чтобы 3 символа совпадали, а не совпадал только один. Вы ошиблись в предсказании. Значение «x» равно 0x78, если вы сдвинете его влево на 3, вы получите 0xC0, который не является «@» с кодом ASCII 0x40. Пожалуйста, проверьте это еще раз. - person Alex Lop.; 05.09.2015
comment
@dasblinkenlight Конечно, вы правы. Виноват. Уже исправил. Спасибо. - person Alex Lop.; 05.09.2015
comment
@AlexLop. Двоичное значение x равно 1111000. Сдвинул на 3 бита влево, что оставило бы нас с 1000000, то есть @. upload.wikimedia.org/wikipedia/commons/d/dd/ ASCII-Table.svg - person alexandernst; 05.09.2015
comment
@alexandernst Вы почти правы. Обратите внимание, что у вас есть двоичное значение длиной 7 бит. Таким образом, на самом деле это 01111000, а сдвиг влево на 3 станет 11000000, что не является '@' - person Alex Lop.; 05.09.2015
comment
@AlexLop. охх, действительно! Какая глупая ошибка! Извини, это моя вина. - person alexandernst; 05.09.2015
comment
@dasblinkenlight Еще раз спасибо! вот что происходит, когда кодить в спешке. Починил это :) - person Alex Lop.; 05.09.2015
comment
@dasblinkenlight Интересно, почему у меня это сработало с _1 _... - person Alex Lop.; 05.09.2015
comment
@AlexLop. Разве это не 11110000 вместо 01111000? Вы добавляете 0 к самому важному биту вместо наименее важного. - person alexandernst; 05.09.2015
comment
Вам нужно добавить 0 со стороны MSB. Например, если у вас есть значение «1», вы представляете его как 00000001, а не 10000000 (что составляет 2 ^ 7! = 1). - person Alex Lop.; 05.09.2015