can — приведение структуры к int в переполнении стека

У меня есть структура, чтобы представить 29-битный идентификатор CAN с битовыми полями, как показано ниже.

struct canId
{
u8 priority         :3;
u8 reserved         :1;
u8 dataPage         :1;
u8 pduFormat        :8;
u8 pduSpecific      :8;
u8 sourceAddress    :8;
} iD;

в моем коде я хочу скопировать эту структуру в целочисленную переменную. Что-то вроде:

int newId = iD;

Однако я не уверен, правильно ли это. Кто-нибудь может это прокомментировать?

Изменить: я могу сделать это с помощью оператора сдвига в каждом поле, а затем с помощью побитового ИЛИ, чтобы они были в нужном месте. Но это делает использование структуры с битовыми полями бесполезным в первую очередь.

6

Решение

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

Но для более удобного решения вполне достаточно объединения int со структурой битовых полей.

Чтобы объединить безопасность и удобство, вы также можете рассмотреть возможность интеграции тестовой функции, которая переключает поля по одному и проверяет совпадение с желаемыми целыми числами.


union
{
uint32_t packed;

struct canId
{
u8 priority         :3;
u8 reserved         :1;
u8 dataPage         :1;
u8 pduFormat        :8;
u8 pduSpecific      :8;
u8 sourceAddress    :8;
} split;
} iD;

iD.packed= 0; iD.split.priority= 7; if (iD.packed != 0x7) ...
3

Другие решения

Я надеюсь, что я правильно сделал немного магии … Вы могли бы сделать что-то вроде:

newID =  (int)iD.priority
| (int)(iD.reserved) << 3
| (int)(iD.dataPage) << (3 + 1)
| (int)(iD.pduFormat) << (3 + 1 + 1)
| (int)(iD.pduSpecific) << (3 + 1 + 1 + 8)
| (int)(iD.sourceAddress) << (3 + 1 + 1 + 8 + 8)

Но для этого вам понадобится система, в которой int имеет не менее 32 бит

2

Оставляя в стороне вопрос о том, что порядок битовых полей определяется реализацией, вы всегда можете добавить битовое поле, чтобы оно стало 32-битным, а затем memcpy это в ваш int:

struct canId
{
u8 priority         :3;
u8 reserved         :1;
u8 dataPage         :1;
u8 pduFormat        :8;
u8 pduSpecific      :8;
u8 sourceAddress    :8;
u8 _padding         :3;
} iD;

int newId = 0;
static_assert(sizeof(iD) <= sizeof(newId), "!");
memcpy(&newId, &iD, sizeof(iD));

Это совершенно четко определенное поведение. Наберите через union не является.

2

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

int canIdToInt(canId id) {
int temp = 0;
int offset = 0;

temp |= id.sourceAddress  << offset; offset += 8;
temp |= id.pduSpecific    << offset; offset += 8;
temp |= id.pduFormat      << offset; offset += 8;
temp |= id.dataPage       << offset; offset += 1;
temp |= id.reserved       << offset; offset += 1;
temp |= id.priority       << offset; // offset += 3; redundant

return temp;
}

Конечно, вы можете скрыть всю смещение за макросом, чтобы сделать его немного чище.

#define START_WRITE int temp=0,offset=0
#define RESULT temp

#define ADD_VALUE(X) temp |= X << offset; offset += sizeof(X)

На самом деле, sizeof не будет вести себя так, как ожидалось, но есть другой макрос что будет.

2
struct Id
{
std::uint32_t bytes;

// only considers least three significant bytes of x
void priority(std::uint8_t x)
{
bytes |= x & 0x03;
}

// only considers the least signifficant byte of x
// sets byte into 4th bit of target value
void reserved(std::uint8_t x)
{
bytes |= ((x & 0x01) << 4);
}

// ...
};

Код клиента:

Id id;
id.priority(0x1);
id.reserved(0x0); // reset the "reserved" bit to zero

В частности, это опасно делать:

u8 priority         :3;
u8 reserved         :1;

Представление этих битов, как вы указываете, зависит от компилятора (и не является частью стандарта).

1

Если выбранная реализация этого формата в OP еще не установлена, возможно, это может помочь.

Вы можете создать класс-оболочку, который управляет словом соответствующего размера и возвращает / устанавливает соответствующие части для вызывающей стороны, как это предлагается user3528438.

Или вы можете сделать то, что я делаю (хотя не для CAN) и создать множественный Обертки: для каждого «подполя» сделайте struct держа uint_or_whatever_t плюс функции-члены /operators, чтобы извлечь соответствующие биты. затем union несколько таких structвместе.

Подождите! Убери свои вилами! Последний шаблон исключение, специально разрешенное Стандартом для чередующихся чтений членов в общая начальная последовательность (посмотрите) стандартного макета structв union — гарантировать, что они определенно обращаются к одной и той же памяти / не оптимизированы.

Определив, что мы находимся в Defined Behavior Land, реальное преимущество состоит в том, как это аккуратно избегает определенной в реализации компоновки «собственных» битовых полей: вы, не ваш компилятор, определите, как соответствующие биты получить / установить. Конечно, вы можете сделать это вручную в C, используя встроенные функции bitmanip или вспомогательные функции — я это делал раньше, — но с помощью класса все это скрыто в реализации класса, как и должно быть.

например

class CanPriority {
// the raw memory
uint32_t m_raw;

public:
// getter/setter methods or operators
uint8_t get() const { /* extract, shift... */ }
// etc.
};

class CanReserved {
uint32_t m_raw;

public:
uint8_t get() const { /* ... */ }
// ...
};

union CanId {
CanPriority priority;
CanReserved reserved;
// etc.
};

Кстати, я сделал довольно дикие вещи с очень сложными версиями этого паттерна для других случаев — представьте, что вы можете сделать, когда templates добавляются, например — и только когда-либо имели прекрасные результаты, даже при -O3 -flto,

1

Я предполагаю, что вы хотели просто взять эти 29 бит из памяти и упаковать их в int за одну операцию приведения.
Боюсь, вы не можете быть уверены, что ваша структура упакована в 29 бит, потому что ссылка говорит:

Несколько смежных битовых полей обычно упаковываются вместе (хотя это поведение определяется реализацией)

http://en.cppreference.com/w/cpp/language/bit_field

Поэтому вам нужно использовать решение fritzone и написать аналогичный код для его преобразования.

Другое возможное решение — изначально обернуть struct в целое и просто обернуть геттеры и сеттеры, чтобы выбрать правильные биты.

0

Если это только C ++, то вы можете использовать reinterpret_cast:

struct canId {
u8 priority         :3;
u8 reserved         :1;
u8 dataPage         :1;
u8 pduFormat        :8;
u8 pduSpecific      :8;
u8 sourceAddress    :8;

int &raw() {
return *reinterpret_cast<int *>(this);
}
} iD;

Затем:

int data = iD.raw();
0