Извлечь верхнее и нижнее слово 32-разрядного целого без знака - PullRequest
0 голосов
/ 21 декабря 2018

Чтобы извлечь верхнее и нижнее слово 32-разрядного целого без знака и сохранить каждое в отдельных переменных uint16_t, я делаю это следующим образом ( nbr - 32-разрядное целое число без знака):

uint16_t lower_word = (uint16_t) nbr & 0x0000FFFF;  // mask desired word
uint16_t upper_word = (uint16_t) ((nbr & 0xFFFF0000) >> 16); // right-shift after masking

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

Ответы [ 4 ]

0 голосов
/ 21 декабря 2018

Если вам нужно повторить эту операцию несколько раз в своем коде, есть другой способ сделать это с помощью объединений:

typedef union _uplow                                                                                     
{                                                                                                        
  struct _reg {                                                                                          
    uint32_t low : 16;                                                                                   
    uint32_t up  : 16;                                                                                   
  } reg;                                                                                                 
  uint32_t word;                                                                                         
} uplow;

Объявите вашу переменную следующим образом:

uplow my_var;
my_var.word = nbr;

Используйте это так:

printf ("Word : 0x%x\n Low : 0x%x\n Up  : 0x%x\n", my_var.word, my_var.reg.low, my_var.reg.up);

Вывод:

Word : 0xaaaabbbb
 Low : 0xbbbb
 Up  : 0xaaaa
0 голосов
/ 21 декабря 2018
uint16_t lower_word = (uint16_t) nbr;
uint16_t upper_word = (uint16_t) (nbr  >> 16);

маски бесполезны

приведение необходимо, иначе компилятор может выдать предупреждение

{изменить, чтобы учесть замечание Лундина / Эрика Постпишиля}

например, gcc -Wconversion выдает предупреждение без приведения

0 голосов
/ 21 декабря 2018

Система типа С является одновременно тонкой и опасной.Явное преобразование может или не может быть необходимым.В частности, (uint16_t) nbr & 0x0000FFFF приведение не является правильным, предполагая 32-битный процессор.

Вы разыгрываете до того, как произойдет операция.Это означает, что операнд nbr будет явно преобразован приведением, а затем немедленно неявно преобразован до int путем неявного целочисленного продвижения.Результат будет иметь тип int, который подписан.Безвреден в этом случае, но может вызвать проблемы в других случаях.Используя неправильный состав, вы сделали signed int из uint32_t, который не был намерением.

В целом вам необходимо знать о неявных правилах продвижения типа .

Несмотря на то, что при присваивании происходит неявное преобразование lvalue обратно в uint16_t, что большую часть времени экономит день.

Также обратите внимание, что 0x0000FFFF - это опасный стиль.Шестнадцатеричные литералы относятся к тому типу, где значение будет соответствовать, независимо от того, сколько нулей вы ставите перед значением.В этом случае это int, который подписан.В 16-битной системе 0x0000FFFF даст int, но 0x00008000 даст unsigned int.(Проверьте эту странную ошибку, например: Почему 0 <-0x80000000? </a>)

Лучшая практика, надежный, переносимый, совместимый с MISRA-C код, - это код, который не содержит никаких неявныхконверсии вообще:

uint32_t nbr = ...;
uint16_t lower_word = (uint16_t) (nbr & 0xFFFFUL);
uint16_t upper_word = (uint16_t) ((nbr >> 16) & 0xFFFFUL);

Предполагается, что nbr известно как uint32_t, в противном случае рекомендуется приводить этот операнд к uint32_t перед приведением.

Маскина самом деле не нужны в этом конкретном случае, но в общем случае, например, когда маскируются 4 байта из uint32_t.

0 голосов
/ 21 декабря 2018

Нет, вам не нужен набор типов, и я бы порекомендовал его не использовать.Это связано с тем, что он имеет более высокий приоритет, чем оператор &, поэтому nbr сначала преобразуется в uint16_t, а затем маскируется.По этой же причине вторая строка не будет работать без дополнительных скобок.

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

...