Как указать символьный литерал, соответствующий MISRA C ++?

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

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

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

Очевидно, сначала я использовал !p, поскольку стандарт гарантирует, что завершение null преобразуется в false (независимо от того, какой тип символа является реальным). Это приводит к ошибке AUTOSAR C ++ 14 (18-03) MISRA.STMT.COND.NOT_BOOLEAN, которая переводится как «Условие оператора if или цикла имеет тип '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('/'), когда я ищу конкретный символ, а не конкретное значение. В этом случае возникает та же ошибка, с жалобой на то, что я не указал суффикс. (uri - это std :: string)

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

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

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


person Maciej Załucki    schedule 02.04.2020    source источник


Ответы (1)


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

Да, но правила MISRA выходят за рамки стандарта. Рассмотрим что-нибудь вроде 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 требуется, если предполагается использовать их в беззнаковой арифметике.

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

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

person Lundin    schedule 02.04.2020
comment
Что ж, это отличный пример того, почему сравнение должно быть явным в таких случаях, конечно, это было *p != '\0', но я пропустил звездочку при написании этого сообщения. Я понимаю правила Misra и Autosar (ну, я не всегда с ними согласен), но этот вопрос, вероятно, больше связан с реализацией фильтра Klocwork, так как это то, что дает ложные срабатывания. По сути, использование символьных литералов, таких как *p != '\0' или myString.find_last_of('/'), вызывает эту MISRA.LITERAL.UNSIGNED.SUFFIX ошибку. Так что в основном я спрашиваю об этой ерунде :) - person Maciej Załucki; 02.04.2020
comment
@ MaciejZałucki Очевидно, что добавление суффикса к символьным константам не имеет смысла. Правило MISRA состоит в том, чтобы добавлять суффикс к целочисленным константам, например 1U вместо 1, в случаях, когда целью является использование беззнаковой арифметики (в частности, побитовые операции). - person Lundin; 03.04.2020