Нарушение правила 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 вместо этого, который четко определен:

Другие вопросы по тегам