Строгое правило алиасинга буфера uint8_t к структуре - PullRequest
0 голосов
/ 17 января 2019

Предположим, у меня есть typedef с битовыми полями, как показано ниже.

typedef struct VIN_oCAN01_3abd61be
{
   uint64_t var1:24;
   uint64_t var2:4;
   uint64_t var3:4
}__attribute__((packed))Message;

И я получаю uint8_t буфер, как показано ниже.

uint8_t buffer[4] = {0x1,0x2,0x3,0x14};

В настоящее время в моей производственной программе моя команда предлагает следующий подход.

Message *ptrMsg = (Message *)buffer;

То есть назначение буфера uint8_t указателю типа Message. Я предположил, что предлагаемый подход не следует правилу строго псевдонимов , и вместо этого мы должны сделать, как показано ниже.

Message msg;
memcpy(&msg,buffer, sizeof(msg));

Обратите внимание, что нет возможности скопировать буфер в структуру вручную (член за членом), так как структура очень большая.

Правильно ли мое понимание? Если да, пожалуйста, предоставьте стандартный документ, который я могу использовать, чтобы доказать свою точку зрения.

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Я предположил, что предлагаемый подход не следует строгому правилу наложения имен

Правильно. ptrMsg = (Message *)buffer означает, что вы не можете получить доступ к данным ptrMsg без вызова неопределенного поведения.

Вы можете доказать свою точку зрения с помощью C17 6.5 §7 (здесь цитируется - Что такое строгое правило псевдонимов? ). Выражение lvalue, такое как ptrMsg->var1 = value, не обращается к сохраненному значению ни через тип, совместимый с эффективным типом того, что там хранится, ни через любое из разрешенных выражений.

Однако вы можете перейти от Message к массиву uint8_t (при условии, что uint8_t является типом символа) без нарушения строгого псевдонима.


Однако более серьезной проблемой является приоритет битового поля, которое является нестандартным и непереносимым. Например, вы не можете знать, какая часть битового поля является MSB и LSB. Вы не можете знать, как биты выровнены в 64-битном типе. Использование 64-битного типа для битового поля является нестандартным расширением. Это зависит от endianess. И так далее.


Если предположить, что 24 бита относятся к битам с 31 по 8 (мы не можем узнать, читая ваш код), тогда правильный код без строгих нарушений псевдонимов, безумия битового поля и нестандартных «убийц структурных заполнителей» будет выглядеть следующим образом :

typedef union
{
   uint32_t var;
   uint8_t  bytes[4];
} Message;


uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t  var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t  var3 = (ptrMsg->var) & 0x0F;

Message, являющийся "типом объединения, который включает один из вышеупомянутых типов среди своих members ". То есть содержит тип, совместимый с uint8_t [4].

Этот код также не содержит копирования, и сдвиги будут переведены в соответствующий битовый доступ в машинном коде.

0 голосов
/ 17 января 2019

Вы правы.

C17 черновик § 6.5:

  1. Объект должен иметь свое сохраненное значение, доступное только через выражение lvalue, которое имеет одно из следующие типы: 89)

    • тип, совместимый с эффективным типом объекта,

    • квалифицированная версия типа, совместимого с эффективным типом объекта,

    • тип, который является типом со знаком или без знака, соответствующим действующему типу объекта,

    • тип, который является типом со знаком или без знака, соответствующим квалифицированной версии действующего тип объекта,

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

В этом случае объект имеет тип uint8_t[], а тип выражения lvalue - Message. Ни одно из вышеуказанных исключений не применимо.

Использование memcpy overrereferences решает эту проблему, или же вы можете отключить строгий псевдоним в вашем компиляторе, если вы хотите писать на языке не-C.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...