Нарушение правила 5-2-7 MISRA C ++ 2008: объект с типом указателя не должен быть преобразован в несвязанный тип указателя, прямо или косвенно

В следующем примере:

bool bad_function()
{
char_t * ptr = 0;

// MISRA doesn't complains here, it allows cast of char* to void* pointer
void* p2 = ptr;

// the following 2 MISRA violations are reported in each of the casts bellow (two per code line)
// (1) Event misra_violation:     [Required] MISRA C++-2008 Rule 5-2-7 violation: An object with pointer type shall not be converted to an unrelated pointer type, either directly or indirectly
// (1) Event misra_violation:     [Required] MISRA C++-2008 Rule 5-2-8 violation: An object with integer type or pointer to void type shall not be converted to an object with pointer type
ptr = (char_t*) (p2);
ptr = static_cast<char_t*> (p2);
ptr = reinterpret_cast<char_t*> (p2);

return true;
}

MISRA 5-2-8 и 5-2-7 нарушений сообщается.

Как я могу устранить это нарушение?

Мне нужен кто-то опытный со статическим анализом C ++, чтобы помочь мне. Я бью себя по голове этими глупыми правилами за несколько дней.

Согласно стандарту MISRA C ++ (MISRA-Cpp-2008.pdf: Правило 5-2-7 (обязательно): объект с типом указателя не должен быть преобразован в несвязанный тип указателя, прямо или косвенно.

Хорошо, но у нас есть много кода, который, например, должен преобразовать адрес в char* а затем использовать его с std::ifstream, который read(char* buffer, int length) Функция требует, чтобы набрать адрес (char_t*). Так как же, по словам ребят из MISRA, кто-то может программировать на C ++ и вообще не использовать приведения? Стандарт не говорит, КАК преобразование указателя тогда должно быть сделано.

В моем рабочем коде мои проблемы заключаются в операциях чтения файлов с использованием чтения с станд: ifstream из файлов в предопределенных структурах данных:

if (file.read((char_t*)&info, (int32_t)sizeof(INFO)).gcount() != (int32_t)sizeof(INFO)
{
LOG("ERROR: Couldn't read the file info header\n");
res = GENERAL_FAILURE;
}

Как предполагается сделать это в соответствии с MISRA?

Так есть ли какие-либо решения вообще?

РЕДАКТИРОВАТЬ: Питер и Q.Q. оба ответа верны, кажется, что MISRA действительно хочет сделать все без каких-либо забросов, что трудно сделать, если проект находится на завершающей стадии. Есть два варианта:

1 — документируйте отклонения MISRA один за другим и объясните, почему приведение в порядке, объясните, как это было проверено (предложение Q.Q.)

2 — используйте байтовый массив из типа char для file.read (), затем после безопасного чтения содержимого файла приведите байтовый массив к содержимому заголовков, это должно быть сделано для каждого элемента по одному, потому что если вы приведете char * к int32_t this Это снова нарушение правила 5-2-7. Иногда это слишком много работы.

4

Решение

Основная причина применения правила MISRA заключается в том, что преобразование любого указателя / адреса в любой не пустой указатель позволяет использовать этот адрес так, как если бы он отличался от того, чем он является на самом деле. Компилятор будет жаловаться на неявное преобразование в этих случаях. Использование Typecast (или C ++ _cast операторы) по существу останавливает компиляцию и, при слишком большом количестве обстоятельств, для разыменования этот указатель дает неопределенное поведение.

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

Вы должны понимать, что философия проверок MISRA меньше заботится о простоте программирования, чем обычные программисты, и больше заботится о предотвращении обстоятельств, при которых неопределенное (или определяемое реализацией, неопределенное и т. Д.) Поведение проходит все проверки и приводит к коду » дикий «, который может причинить вред.

Дело в том, что в вашем реальном случае вы полагаетесь на file.read() правильно заполнить (предположительно) структуру данных с именем info,

if (file.read((char_t*)&info, (int32_t)sizeof(INFO)).gcount() != (int32_t)sizeof(INFO)
{
LOG("ERROR: Couldn't read the file info header\n");
res = GENERAL_FAILURE;
}

Что вам нужно сделать, так это немного усерднее работать над созданием правильного кода, который пройдет проверку MISRA. Что-то вроде

std::streamsize size_to_read = whatever();
std::vector<char> buffer(size_to_read);

if (file.read(&buffer[0], size_to_read) == size_to_read)
{
//  use some rules to interpret contents of buffer (i.e. a protocol) and populate info
// generally these rules will check that the data is in a valid form
//   but not rely on doing any pointer type conversions
}
else
{
LOG("ERROR: Couldn't read the file info header\n");
res = GENERAL_FAILURE;
}

Да, я понимаю, что это больше, чем просто использовать преобразование типов и разрешать двоичное сохранение и чтение структур. Но их перерывы. Помимо прохождения проверки MISRA, у этого подхода есть и другие преимущества, если вы все сделаете правильно, например, формат файла полностью независим от того, какой компилятор используется для сборки вашего кода. Ваш код зависит от заданных реализацией величин (расположение элементов в структуре, результаты sizeof) поэтому ваш код — если он построен с помощью компилятора A — может быть не в состоянии прочитать файл, сгенерированный кодом, созданным с помощью компилятора B. И одна из общих тем требований MISRA — сокращение или устранение любого кода с поведением, которое может быть чувствительным к реализации. определенные количества.

Примечание: вы также проходите char_t * в std::istream::read() в качестве первого аргумента и int32_t как второй аргумент. Оба на самом деле неверны. Фактические аргументы имеют тип char * а также std::streamsize (который может быть, но не обязательно должен быть int32_t).

1

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

Преобразование несвязанных указателей в char* это не хорошая практика.
Но если у вас большая унаследованная кодовая база, делающая это часто, вы можете подавить правила, добавив специальные комментарии.

1

fread это очень хорошая функция C ++ для ввода файлов, и она использует void*, что позволяет MISRA.

Это также хорошо для чтения двоичных данных, в отличие от fstream который обрабатывает все данные с помощью локализованной логики преобразования символов (это «фасет» в iostream, который настраивается, но в стандарте не определен какой-либо переносимый способ достижения преобразования без операции).

С-стиль fopen/fclose неудачно в программе на C ++, так как вы можете забыть очистить ваши файлы. К счастью, у нас есть это std::unique_ptr который может добавить функциональность RAII к произвольному типу указателя. использование std::unique_ptr<FILE*, decltype(&fclose)> и иметь быстрый исключительный бинарный файловый ввод / вывод в C ++.


NB: распространенным заблуждением является то, что std::ios::binary дает бинарный файл ввода / вывода. Это не. Все это влияет на преобразование новой строки и (в некоторых системах) обработку маркера конца файла, но это не влияет на преобразование символов, управляемое фасетами.

0
По вопросам рекламы [email protected]