В настоящее время я работаю над проектом, в котором мне предоставляются следующие структура. Моя работа написана на C++, но в проекте используются как C, так и C++. Та же структура определение используется как C, так и C++.
typedef struct PacketHeader {
//Byte 0
uint8_t bRes :4;
uint8_t bEmpty :1;
uint8_t bWait :1;
uint8_t bErr :1;
uint8_t bEnable :1;
//Byte 1
uint8_t bInst :4;
uint8_t bCount :3;
uint8_t bRres :1;
//Bytes 2, 3
union {
uint16_t wId; /* Needed for Endian swapping */
struct{
uint16_t wMake :4;
uint16_t wMod :12;
};
};
} PacketHeader;
В зависимости от того, как используются экземпляры структуры, требуемый порядок байтов
структура может быть с прямым или прямым порядком байтов. Поскольку первые два байта
структура представляет собой каждый отдельный байт, их не нужно изменять, когда порядок байтов
изменения.
Байты 2 и 3, хранящиеся как одиночные uint16_t, — единственные байты, которые нам нужны.
swap, чтобы добиться желаемого порядка байтов. Чтобы добиться замены порядка байтов, мы имеем
выполнял следующее:
//Returns a constructed instance of PacketHeader with relevant fields set and the provided counter value
PacketHeader myHeader = mmt::BuildPacketHeader(count);
uint16_t packetIdFlipped;
//Swap positions of byte 2 and 3
packetIdFlipped = myHeader.wId << 8;
packetIdFlipped |= (uint16_t)myHeader.wId >> 8;
myHeader.wId = packetIdFlipped;
Функция BuildPacketHeader(uint8_t) присваивает значения членам wMake и
wMod явно и не пишет участнику wId. Мой вопрос касается
безопасность чтения члена wId внутри возвращенного экземпляра
состав.
Такие вопросы, как Доступ к неактивному члену союза и неопределенное поведение?, Назначение союзов в C и C++, и Раздел 10.4 проекта стандарта, который у меня есть, упоминает неопределенное поведение, возникающее при доступе к неактивному члену объединения в C++.
Параграф 1 раздела 10.4 связанного проекта также содержит следующее примечание, хотя я не уверен, что понимаю всю используемую терминологию:
[Примечание: для упрощения использования объединений делается одна специальная гарантия: если объединение стандартной компоновки содержит несколько структур стандартной компоновки, которые имеют общую начальную последовательность (10.3), и если нестатический элемент данных объекта этот тип объединения стандартной компоновки активен и является одной из структур стандартной компоновки, разрешено проверять общую начальную последовательность любого из членов структуры стандартной компоновки; см. 10.3. — последнее примечание]
Чтение myHeader.wId в строке packetIdFlipped = myHeader.wId << 8 неопределенное поведение?
Является ли безымянная структура активным членом, поскольку она была последним членом, записанным при вызове функции?
Или это примечание означает, что доступ к члену wId безопасен, поскольку он и структура имеют общий тип? (и это и есть то, что подразумевается под общей начальной последовательностью?)
заранее спасибо
@eerorika Спасибо - я даже не понял этого. Изучив его, я получил комментарий Подобно объединению, безымянный член структуры, тип которой является структурой без имени, называется анонимной структурой. Каждый член анонимной структуры считается членом объемлющей структуры или объединения. Это применяется рекурсивно, если окружающая структура или объединение также являются анонимными.здесь. Означает ли это, что приведенный выше код на самом деле не является UB, поскольку каждый член внутренней структуры и объединения считается членами struct PacketHeader?
Это УБ. Анонимность союза ничего не меняет.
Кажется, я не нашел кнопку редактирования комментария, но при более внимательном чтении я думаю, что эта ссылка все равно относилась к C. Большое спасибо, еще раз





Is reading
myHeader.wIdin the linepacketIdFlipped = myHeader.wId << 8undefined behaviour?
да. Вы назначили wMake и wMod, сделав безымянную структуру активным элементом, поэтому wId является неактивным элементом, и вам не разрешено читать из него, не установив для него значение.
and is this what is meant by common initial sequence?
общая начальная последовательность — это когда два стандартные типы макетов совместно используют одни и те же элементы в одном и том же порядке. В
struct foo
{
int a;
int b;
};
struct bar
{
int a;
int b;
int c;
};
a и b имеют один и тот же тип в foo и bar, поэтому они являются их общей начальной последовательностью. Если вы поместите объекты foo и bar в союз, будет безопасно читать a или b из объекта with после того, как он установлен в одном из них.
Это не ваш случай, поскольку wId не является стандартной структурой макета.
Достаточно ли обернуть wId в struct ID{ ... }, чтобы получить стандартный тип макета? И, если да, будет ли он разделять общая начальная последовательность со следующей анонимной (в приведенном примере — будет изменена теперь, когда eeorika указала, что она плохо определена) структурой, поскольку каждая из них состоит из uint16_t начального члена? Или тот факт, что элемент последней структуры является битовым полем, означает, что они не являются общими типами?
Я должен прочитать более внимательно, прежде чем задавать больше вопросов, мои извинения. Ссылки, которые вы разместили, вели на другую страницу, уточняя и если это битовые поля, они имеют одинаковую ширину.
@cprlkleg Это все равно было бы незаконно. Как вы выяснили, обе структуры должны иметь битовое поле, чтобы быть законными.
The function BuildPacketHeader(uint8_t) assigns values to the members wMake and wMod explicitly, and does not write to the member wId. My question is regarding the safety of reading from the member wId inside the returned instance of the structure.
Да, это УБ. Это не значит, что он не работает, просто он может не работать. Чтобы избежать этого, вы можете использовать memcpy внутри BuildPacketHeader (см. это и это).
Спасибо за ответ и подсказку memcpy. Является ли это все еще UB, учитывая, что внутреннее объединение и структура являются анонимными (как указано eeorika), и следующий комментарий Подобно объединению, безымянный член структуры, тип которой является структурой без имени, называется анонимной структурой. Каждый член анонимной структуры считается членом объемлющей структуры или объединения. Это применяется рекурсивно, если окружающая структура или объединение также являются анонимными., источник EDIT: ссылка, похоже, относится к C в любом случае, мои извинения
То, что говорит стандартный вид С++, - это две структуры A и B и следующее объединение:
union U
{
A a;
B b;
};
Ниже приведен допустимый код:
U u;
A a;
u.a = a;
a = u.a;
B b;
u.b = b;
b = u.b;
Вы читаете и пишете однотипно. Это, очевидно, правильный код.
Но проблема возникает, когда у вас есть следующий код:
A a;
B b;
u.a = a;
b = u.b;
Что мы знаем об А и Б? Сначала в объединении они делят одно и то же пространство памяти. Теперь стандарт C++ прямо объявил это поведение неопределенным.
Но это не значит, что это полностью из окна. C99 вступает в игру, так как это нормативная база и есть слабые гарантии по союзам. То есть, если член объединения имеет одинаковую структуру памяти, они совместимы, и каждый первый адрес памяти структуры одинаков. Поэтому, если вы можете убедиться, что все ваши структуры/члены объединения заполнены правильно, операция безопасна, даже если С++ говорит, что она не определена.
Наконец, с прагматической точки зрения, если вы не возитесь с отступами и получите стандартный макет, компилятор, как правило, поступит правильно, поскольку это довольно старый шаблон использования в C, и его нарушение приведет к поломке МНОГО кода.
Анонимные структуры плохо сформированы в C++. т.е. это:
struct{ /* members */ };