Проблемы с байт-обращением целого числа - PullRequest
2 голосов
/ 19 сентября 2010

Godday all.

Может, кто-нибудь объяснит логическую разницу в этих двух реализациях функции обращения байтов.

Пример 1:

uint32_t byte_reverse_32(uint32_t num) {
    static union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
    bytes.n = num;

    uint32_t ret = 0;
    ret |= bytes.b[0] << 24;
    ret |= bytes.b[1] << 16;
    ret |= bytes.b[2] << 8;
    ret |= bytes.b[3];

    return ret;
}  

Пример 2:

uint32_t byte_reverse_32(uint32_t num) {
    static union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
    bytes.n = num;

    uint32_t ret = 0;
    ret |= (bytes.b[0] << 24) || (bytes.b[1] << 16) || (bytes.b[2] << 8) || (bytes.b[3]);
    return ret;
}

Я должен что-то упустить, потому что для целого числа без знака 0xFE12ADCF первый пример правильно дает 0xCFAD12FE, а второй - 1.Что мне не хватает?

Кстати, я не мог понять, как получить «<<» внутри pre + code-tags, отсюда <code>LSHIFT.Если это возможно, смело редактируйте (и комментируйте как =)).

Спасибо.

РЕДАКТИРОВАТЬ: Исправлена ​​переменная bytes, котораяникогда не был назначен.

Ответы [ 8 ]

10 голосов
/ 19 сентября 2010

| не тот же оператор, что и ||.Первый - побитовое ИЛИ, то, что вы хотите.Второе - логическое ИЛИ, которое у вас есть.

3 голосов
/ 20 сентября 2010

Следует отметить, что весь подход к целому байт-обращению путем смешивания физического уровня доступа к памяти и значения-уровня побитовых операций выглядит весьма подозрительным.Я имею в виду, это может сработать для вас, но зачем кому-то так поступать?Какой смысл создавать такой странный микс?

Если вы решили прибегнуть к прямому доступу к физической памяти, переосмыслив целочисленное значение как последовательность из 4 байтов, естественным способом обращения было бы просто поменяться местами.байты 0 и 3 и поменяйте местами байты 1 и 2.

uint32_t byte_reverse_32(uint32_t num) {
    union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
    uint8_t t;

    bytes.n = num;

    t = bytes.b[0]; bytes.b[0] = bytes.b[3]; bytes.b[3] = t;
    t = bytes.b[1]; bytes.b[1] = bytes.b[2]; bytes.b[2] = t;

    return bytes.n;
}

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

uint32_t byte_reverse_32(uint32_t num) {
    uint32_t res;

    res = num & 0xFF; num >>= 8; res <<= 8;
    res = num & 0xFF; num >>= 8; res <<= 8;
    res = num & 0xFF; num >>= 8; res <<= 8;
    res = num & 0xFF; 

    return res;
}

Оба вышеперечисленных подхода, если они реализованы правильно, будут переносимыми в том смысле, что они будут работать как на платформах с большими, так и на младших порядках байтов.Люди обычно обращаются к побитовому подходу на уровне значений специально для того, чтобы избегать реинтерпретации физической памяти через объединение, так как реинтерпретация памяти почти всегда является хаком.(то есть оба подхода, смешанные вместе), кажется, имеют очень мало смысла (если вообще есть).То, что у вас есть сейчас, будет выполнять обращение на платформе с прямым порядком байтов, но ничего не будет делать на платформе с прямым порядком байтов.Я понимаю, что вас могут не интересовать платформы с прямым порядком байтов, поэтому проблема для вас не критична.Но все же сочетание двух альтернативных (и обычно взаимоисключающих) техник мне кажется довольно странным.

Опять же, если вы уже решили использовать подход, основанный на объединении, просто поменяйте местами байты и покончите сэто.

1 голос
/ 07 марта 2012

Возможно, вы захотите использовать системные заголовки (endian.h в glibc, sys/endian.h и machine/endian.h на BSD), которые определяют bswap_32 (или то же самое под немного другим именем) для вашей архитектуры.У некоторых процессоров есть инструкция для этого, которая выполняется быстрее, чем перестановка битов.

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

1 голос
/ 19 сентября 2010

Вы вообще не используете параметр num, а вместо этого используете неинициализированную переменную объединения bytes.

Объединение здесь на самом деле не нужно, вы можете просто привести параметр num таким образом, чтобы получить доступ к его байтам:

uint8_t *b = (uint8_t*)&num;
// then access b[0] through b[3]    

И как уже отвечали другие парни, используйте битовый оператор | вместо ||.

0 голосов
/ 20 января 2017

Работает отлично !!!Обратите внимание, что здесь int 4 байта.вход 0xAABBCCDD Выход 0xDDCCBBAA

# include

int main()
{

    int A= 0x12ABCDEF;
    int R=0;
    R = R | ((A&0x000000ff) << 24);
    R = R | ((A&0x0000ff00) << 8);
    R = R | ((A&0x00ff0000) >> 8);
    R = R | ((A&0xff000000) >>24);

    printf("A is %x  R is %x \n", A, R);

    return 0;
}
0 голосов
/ 19 сентября 2010

На вопрос уже был дан ответ, но только мои 2 цента о коде, который может войти в производство:

Используя ключевое слово static в своем коде, как показано ниже:

uint32_t byte_reverse_32(uint32_t num) {
    static union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
/* etc.*/

не имеет никакого значения производительности (или какого-либо значения в любом случае) и гарантирует, что ваша функция будет иметь гонки данных, если вызывается из нескольких потоков (то есть ее использование будет небезопасно).

Удалите static: Ваша функция будет максимально быстрой и безопасной для многопоточности (не говоря уже о более простых).

uint32_t byte_reverse_32(uint32_t num) {
    union bytes {
        uint8_t b[4];
        uint32_t n;
    } bytes;
/* etc.*/
0 голосов
/ 19 сентября 2010

Вы используете ||который является логическим или оператором, вы хотите двоичный |оператор.

0 голосов
/ 19 сентября 2010

Вы используете ||, который является логическим оператором, который имеет значение true (1), если один из двух аргументов отличается от нуля.

Чтобы получить правильные результаты, вам нужен побитовый оператор ИЛИ, который |, как вы это делали в первом примере (x | = y - это x = x | y, а не x = x || y).

Итак, второй пример должен работать с

ret |= (bytes.b[0] LSHIFT 24) | (bytes.b[1] LSHIFT 16) | (bytes.b[2] LSHIFT 8) | (bytes.b[3]);

Надеюсь, это поможет.

...