C ++: объединение против побитовых операторов - PullRequest
4 голосов
/ 15 августа 2010

У меня есть два char, и я хочу «сшить» их по кусочкам вместе.
Например:

char c1 = 11; // 0000 1011
char c2 = 5;  // 0000 0101
short int si = stitch(c1, c2); // 0000 1011 0000 0101

Итак, я впервые попробовал побитовые операторы:

short int stitch(char c1, char c2)
{
    return (c1 << 8) | c2;
}

Но это не работает: я получаю short равное c2 ... (1) Почему?
(Но: c1 и c2 - отрицательные числа в моем реальном приложении ... может быть, это часть проблемы?)

Итак, моим вторым решением было использование union:

union stUnion
{
    struct
    {
         char c1;
         char c2;
    }
    short int si;
}

short int stitch(char c1, char c2)
{
    stUnion u;
    u.c1 = c1;
    u.c2 = c2;
    return u.si;
}

Это работает так, как я хочу ... Я думаю

(2) Какой самый лучший / быстрый способ?

Спасибо!

Ответы [ 5 ]

7 голосов
/ 15 августа 2010

Метод union в лучшем случае определяется реализацией (на практике он будет довольно надежно работать, но формат si зависит от порядкового номера платформы).

Проблема с побитовой передачейпуть, как вы подозреваете, отрицательные числа.Отрицательное число представлено цепочкой ведущих 1.Так, например, -5 это

1111 1011

Если вы приведете это значение к int или даже unsigned int, оно станет

1111 1111 1111 … 1111 1011

, и все эти 1 будут заглушены слева.смещенные данные при применении ИЛИ.

Чтобы решить эту проблему, приведите char s к unsigned char, а затем к int (чтобы предотвратить переполнение или даже появление возможности переполнения) доshifting:

short int stitch(char c1, char c2)
{
    return ( (int) (unsigned char) c1 << 8) | (unsigned char) c2;
}

или, если вы можете изменять типы аргументов, и вы можете включить <cstdint>,

uint16_t stitch( uint8_t c1, uint8_t c2)
{
    return ( (int) c1 << 8 ) | c2;
}
3 голосов
/ 15 августа 2010

$ 5.8 / 1 состояния- "Операнды должны быть целочисленного типа или типа перечисления, и выполняются интегральные преобразования. Тип результата - тип повышенного левого операнда. Поведение не определено, если правый операнд отрицательный или повторный больше или равно длине в битах повышенного левого операнда. "

Так что попытайтесь типизировать c1 к unsigned int и затем побитовое ИЛИ с C2. Также верните вывод как unsigned int. символы преобразуются в int, но мы хотим быть «unsigned int»

2 голосов
/ 15 августа 2010

Причина в том, что c2 сначала переводится в int перед выполнением побитового ИЛИ, что приводит к расширению знака (при условии, что символ подписан и может содержать отрицательные значения):

char x1 = -2; // 1111 1110
char x2 = -3; // 1111 1101

short int si = stitch(c1, c2); // 1111 1111 1111 1101

Представление x2, повышенное до int, (как минимум) заполнено на 1 байт 1, поэтому оно перезаписывает нулевой бит x1, который вы ранее сдвинули вверх. Сначала вы можете разыграть unsigned char. С двумя представлениями дополнения это не изменит битовый шаблон в младшем байте. Хотя это и не является строго необходимым, вы также можете привести c1 к unsigned char для согласованности (если длина short составляет 2 байта, не имеет значения, что знак c1 был расширен за пределы этих 2 байтов)

short int stitch(char c1, char c2) {
    return ((unsigned char)c1 << 8) | (unsigned char)c2;
}
1 голос
/ 15 августа 2010

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

Кроме того, метод объединения, вероятно, медленнее на многих современных процессорах из-за проблемы пересылки из хранилища в загрузку (STLF). Вы записываете значение в память, а затем читаете его обратно как различные типы данных. Многие процессоры не могут быстро отправить данные в загрузку, если это произойдет. Загрузка должна ждать, пока хранилище не будет полностью завершено (удалено), записывая свои данные в кэш L1.

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

0 голосов
/ 15 августа 2010

Вы не должны использовать союз для этого. Вы никогда не должны использовать поля объединения одновременно. Если профсоюз имеет члена A и участника B, то вы должны учитывать, что A и B не связаны между собой. Это потому, что компилятор может добавлять отступы везде, где захочет (кроме как в начале struct). Другая проблема - порядок байтов (little / big endian).

// EDIT Существует исключение из вышеприведенного «правила объединения», вы можете использовать эти элементы одновременно, которые находятся на переднем плане и имеют одинаковую структуру. т.е.

union {
    struct {
        char c;
        int i;
        short s;
    } A;
    struct {
        char c;
        int i;
        char c1;
        char c2;
    } B;
};

A.c и A.i могут использоваться одновременно с B.c и B.i

...