Приращение MISRA в C

При отладке некоторого встроенного кода я наткнулся на что-то вроде этого:

buffPtr = &a[5];
buffEndPtr = &a[10];

while (buffPtr != buffEndPtr) 
{ 
    *buffPtr = 0xFF; 
    buffPtr  = &buffPtr[1];         /*  MISRA improvement for: buffPtr++ */ 
}

Почему эта конструкция будет улучшением (* buffPtr) ++?

Ответ 1

Существует правило MISRA, в котором указывается, что только допустимая математика указателя - это операция индексирования.

Образец, который вы показали, является плохо выполненным обходом. Это уродливое/странное/необычное и, вероятно, основанное на непонимании цели этого правила. Это может также нарушить другое правило.

Лучший способ написать этот код:

for(i=5; i < 10; i++)
{
    a[i] = 0xff;
}

Обновление 2015-05-20 - Поскольку это был принятый ответ, здесь нарушено фактическое правило, любезно предоставлено embedded.kyle:

MISRA-C: 2004, Правило 17.4 (обязательно) или MISRA-C: 2012, Правило 18.4 (обязательно) Индексирование массива должно быть единственной допустимой формой арифметики указателя.

Ответ 2

Правило, нарушающее (*buffPtr)++, следующее:

MISRA-C: 2004, Правило 17.4 (обязательно) или MISRA-C: 2012, Правило 18.4 (обязательно)

Индексирование массива должно быть единственной допустимой формой арифметики указателя.

Их аргументы в пользу этого правила:

Индексирование массива с использованием синтаксиса подстроки массива, ptr[expr], является предпочтительная форма арифметики указателя, поскольку она часто более ясна и следовательно, меньше ошибок, чем манипуляции с указателями. Любые явно рассчитанное значение указателя имеет потенциал для доступа к непредвиденным или неверные адреса памяти. Такое поведение также возможно с помощью массива индексирование, но синтаксис подстроки может облегчить задачу ручного просмотра.

Арифметика указателя в C может ввести в заблуждение новичка. Выражение ptr+1 можно ошибочно интерпретировать как добавление 1 к адрес, расположенный в ptr. Фактически новый адрес памяти зависит от размер в байтах цели-указателя. Это недоразумение может привести к непредвиденному поведению, если sizeof применяется неправильно.

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

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

Ответ 3

В правиле 17.4 MISRA-C: 2004 существовало консультативное правило, запрещающее все формы арифметики указателя. Цель была хороша, цель этого правила заключалась в попытке запретить потенциально опасный код, например:

stuff* p; 
p = p + 5; // 5 stuff, not 5 bytes, bug or intentional?

и трудночитаемый код, например

*(p + 5) = something;  // harder to read but equivalent to p[5]

Как правило, цель состоит в том, чтобы рекомендовать использовать целочисленный итератор вместо арифметики указателя при переходе по заостренным данным.

Однако правило также запретило различные операции с основным указателем, которые, вероятно, не опасны, например ptr++. Как правило, правило было слишком строгим.

В MISRA-C: 2012 это правило (18.4) было ослаблено, чтобы запретить только операторы + - += -=.


В вашем случае buffPtr = &buffPtr[1]; была ошибочной попыткой уклониться от правила 17.4, потому что правило не имело большого смысла. Вместо этого программист решил обфускать свою программу, делая ее менее читаемой и, следовательно, менее безопасной.

Правильный способ справиться с этим - это использовать оператор ++ и игнорировать правило 17.4. Это консультативное правило, поэтому никаких отклонений не требуется (за исключением случаев, когда локальная реализация MISRA-C по какой-либо причине говорит об ином). Если вам нужно отклониться, вы можете просто сказать, что правило не имеет никакого смысла для оператора ++, а затем ссылается на MISRA-C: 2012 18.4.

(Конечно, переписывание всего цикла в цикл for, как показано в другом ответе, является лучшим решением)

Программирование без использования здравого смысла всегда очень опасно, так как слепо следит за MISRA, не понимая разумного обоснования правил или в этом случае недостатка.