Нарушение правила 11.3 MISRA C-2012 при попытке выполнить приведение типа от указателя к типу int
Я пытаюсь избавиться от правила 11.3 из своего кода.
Образец кода:
static int32_t
do_test(const char *cp)
{
const char *c = cp;
const int32_t *x;
x = (const int32_t *)cp;
return *x;
}
Я хочу, чтобы значения *c и *x были одинаковыми. Даже если код компилируется и выдает правильное значение, "x = (int32_t *)cp;" вызывая нарушение 11.3 и 11.8
Нарушение правила 11.3. Объект с типом указателя не должен быть преобразован в указатель на другой тип объекта.
Я пытался с пустым указателем, но результат был не таким, как я ожидал, и это привело к дополнительному нарушению.
Есть ли способ устранить эти нарушения?
Из Документа MISRA C 2012 они упоминают, как будто есть исключение для этого правила, поскольку разрешено преобразовывать указатель на тип объекта в указатель на один из типов объекта char, подписанный символ или неподписанный символ. Стандарт гарантирует, что указатели на эти типы могут использоваться для доступа к отдельным байтам объекта.
Игнорируйте Dir 4.6 из-за типа символа.
3 ответа
Вам повезло, что вы используете MISRA-C, потому что этот код полон ошибок. Вы не можете заставить жуков уйти с актерами.
Ошибка 1. Символьный указатель не обязательно выровнен, и в этом случае ваш код вызывает неопределенное поведение в соответствии со стандартом C 6.3.2.3/7:
Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель неправильно выровнен для ссылочного типа, поведение не определено.
Ошибка 2. Код содержит явное нарушение строгого алиасинга. Это всегда неопределенное поведение в соответствии со стандартом C 6.5/7.
Ваше предположение "Стандарт гарантирует, что указатели на эти типы могут использоваться для доступа к отдельным байтам объекта". правильно: в качестве специального исключения C позволяет вам преобразовывать указатель в x в указатель на char и затем получать доступ к данным через указатель char. Но не наоборот.
Ваш код не имеет доступа к отдельным байтам; Вы идете наоборот, от массива символов до 32-битного типа. Это не разрешено Смотрите Что такое строгое правило псевдонимов?,
Правильный код, который должен подойти как для языка C, так и для MISRA-C:
static int32_t do_test(const char *cp)
{
return (int32_t) ((uint32_t)cp[0] << 24u) |
((uint32_t)cp[1] << 16u) |
((uint32_t)cp[2] << 8u) |
((uint32_t)cp[3]);
}
Эта версия сдвига всегда предпочтительна, так как она не зависит от порядка байтов и, следовательно, переносима. Броски к uint32_t
необходимы для предотвращения неявного продвижения в 8/16-битных системах, плюс вы никогда не должны делать сдвиг битов на подписанных типах.
Если вы чувствуете необходимость избежать явного приведения, вы всегда можете сделать memcpy
:
#include <string.h>
#include <stdint.h>
static int32_t
do_test(const char *cp)
{
int32_t r;
memcpy(&r,cp,sizeof(r));
return r;
}
С оптимизирующим компилятором, который имеет встроенный mempcy
, это должно быть так же эффективно, как return *(int32_t*)cp;
(ваш код написан более лаконично).
Имейте в виду, что в любом случае код определяется только в том случае, если cp
значение, которое вы передали в баллах к действительному int32_t
объект.
Если memcpy не в порядке из-за неявного char*
в void*
бросить, вы могли бы заменить его на заказ, выполненный тривиально void charwise_memcpy(char *Dest, char const *Src, size_t Sz);
или эквивалент for
петля.
void charwise_memcpy(char *Dest, char const *Src, size_t Sz)
{
for(size_t i=0; i<Sz; i++)
Dest[i]=Src[i];
}
Исходный код может вызвать неопределенное поведение:
const char *cp;
// ...
x = (const int32_t *)cp;
Если int32_t
имеет требование выравнивания на платформе, и cp
неправильно выровнен для этого требования, поведение не определено.
Я не фанат MISRA в целом, но этот конкретный случай кажется вполне оправданным. Даже если вы оказались на платформе без требований выравнивания (маловероятно даже во встроенной разработке), этот код непереносим и может начать работать, если вы перейдете на ЦП, который имеет требования выравнивания.
Хорошим решением является использование memcpy
вместо этого, который четко определен: