Как указать символьный литерал, соответствующий MISRA C ++? - PullRequest
1 голос
/ 02 апреля 2020

Я организую правила Klocwork и устраняю любые проблемы, обнаруженные при анализе stati c. Применено несколько правил, и в настоящее время у меня проблема с указанием литералов символов. Давайте рассмотрим этот пример:

for (const char* p = str; *p != '\0'; ++p)

Как видите, это l oop, повторяющаяся по C -String. Он используется для использования unordered_map строковых литералов constexpr. Измерения производительности показали, что хранение их как std :: string увеличивает использование памяти и влияет на производительность из-за накладных расходов. Поскольку содержимое этой карты является постоянным, я использую пользовательский функтор ha sh для C -Strings (опять же, чтобы избежать преобразований и копирования строки в std :: string просто для генерации ha sh). Простой ответ будет использовать std :: string_view, но он не доступен в этой среде. Таким образом, проблема исходит из самого условия. Условие должно проверять, завершается ли символ нулем.

Очевидно, что сначала я использовал !p, так как по стандарту гарантируется, что завершение нуля преобразуется в ложь (независимо от того, что является реальным типом символа). Это приводит к ошибке AUTOSAR C ++ 14 (18-03) MISRA.STMT.COND.NOT_BOOLEAN, которая переводится в « Условие оператора if или l oop имеет тип« char »вместо« boolean »».

Хорошо, тогда я изменил это на явное сравнение p != 0, и оказалось, что это MISRA.CHAR.NOT_CHARACTER нарушение, которое " 'char' используется для не символьного значения ".

Опять же, это верный момент, так как я сравниваю char с int, но char не является ни int, ни unsigned int. Поэтому я изменил его на *p != '\0', который должен напрямую переводиться в нулевой символ. Это, в свою очередь, дает нарушение MISRA.LITERAL.UNSIGNED.SUFFIX, которое равно " Целочисленный литерал без знака '' \ 0 '' без суффикса 'U' ". Теперь я удивлен. Даже если char считается неподписанным в одном компиляторе, он не гарантированно подписан или не подписан, поэтому я не могу жестко закодировать его для любого знака. Даже не говоря о том, что, похоже, нет способа указать суффикс для символьных литералов. По моему мнению, это уже ложно-положительный тип '\0' IS типа char и не должен требовать дальнейшего преобразования или приведения. Это показывает еще более заметную проблему с синтаксисом, таким как uri.find_last_of('/'), где я ищу указанный c символ, а не конкретное значение. Этот случай генерирует ту же ошибку, жалуясь, что я не указал суффикс. (uri - это std :: string)

Я полагаю, что это ложно-положительный результат реализации фильтра с ошибками Также кажется, что анализ stati c может быть неправильно настроен, так как символьные литералы считаются целочисленными только в C, а не в C ++.

В качестве примечания добавлю, что в первом примере, используя *p != char(0) решил эту проблему, но это далеко от предпочтительного решения и может использоваться только с известным целочисленным значением символа, которое гораздо менее гибкое и подвержено ошибкам, чем использование литералов, поэтому я не собираюсь использовать этот обходной путь.

Что ваши мысли по этому поводу? Возможно, кто-то уже получил такую ​​ошибку Klocwork и нашел решение, отличное от отключения правила или его подавления для каждого экземпляра буквенного символа. У меня уже есть список распространенных ложных срабатываний, которые довольно часто исходят из C ++ 11 и более новых стандартов, проверенных правилами, основанными на MISRA 2008 C ++.

1 Ответ

2 голосов
/ 02 апреля 2020

Поначалу, очевидно, я использовал! P, так как по стандарту гарантируется, что завершение нуля разрешается в false

Да, но MISRA правил go вне стандарта. Рассмотрим что-то вроде char* ptr = 0; if(ptr). Можно легко проскользнуть между if(ptr) и if(*ptr), и это типичный источник ошибок. Является ли код правильным или ошибочным, читатель не может определить намерение программиста только из этой строки.

Точно так же, каково намерение с ptr != 0? Чтобы проверить, является ли указатель NULL, или данные, на которые указывает указатель, равны нулю, или если данные, в частности, являются нулевым терминатором в конце строки?

Поэтому MISRA осуществляет явную проверку. Код, подобный if(ptr != NULL) или if(*ptr != '\0'), является рекомендацией MISRA, и здесь намерение программиста совершенно ясно.

Ваш вопрос имеет эту проблему повсюду! Вы вводите *p в некоторых местах и ​​p в некоторых местах. const char* p = str; ... p != '\0', очевидно, является ошибкой, и, если это ваш настоящий код, то MISRA просто спасла вас от него.

Поэтому я изменил его на p != '\0', что должно напрямую переводиться в нулевой символ.

Действительно, это код, совместимый с MISRA. Опять же, если предположить, что p равно char, а не char*.

Это, в свою очередь, приводит к нарушению MISRA.LITERAL.UNSIGNED.SUFFIX, которое представляет собой "беззнаковое целочисленное литеральное значение" \ 0 "без суффикс 'U' ".

Это чепуха. '\0' является символьной константой и имеет тип char в C ++. Ваш инструмент должен путать это с обычными целочисленными константами (десятичными, восьмеричными или шестнадцатеричными), где требуется суффикс U, если предполагается использовать их в беззнаковой арифметике c.

Теперь MISRA хмурится на восьмеричных escape-последовательностях в общем, но в MISRA- C: 2004 многие люди (включая вас искренне) указали комитету, что \0 должно быть сделано действительным исключением. Это было исправлено, и \0 был признан действительным в MISRA- C: 2004 TC1, опубликованном в июле 2007 года. Я не уверен, что это исправление вошло в оригинальную MISRA-C ++: 2008 или есть T C для MISRA-C ++.

В любом случае, использование '\0' для нулевого терминатора вполне нормально и соответствует требованиям MISRA. Пока вы используете его только для сравнения с другими операндами типа char.

...