c указатель декремент безопасно / небезопасно?
Анализируя некоторый код:
static volatile UCHAR *pucSndBufferCur;
eMBErrorCode eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
if( eRcvState == STATE_RX_IDLE )
{
/* First byte before the Modbus-PDU is the slave address. */
pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucSndBufferCur[0] = ucSlaveAddress;
Это выглядит небезопасно, что, если память в адресе (pucFrame - 1) уже используется для другой переменной, и перезапись может привести к сбоям.
Как вы думаете, можно ли использовать подобный код или его неправильный способ, и его никогда не следует использовать?
4 ответа
Из этого комментария
/* First byte before the Modbus-PDU is the slave address. */
pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
мы можем сделать вывод, что функция ожидает, что вызывающая сторона гарантирует это. В этом случае, если вызывающий абонент выполняет требование, код является безопасным.
Если вызывающая сторона не гарантирует этого, функция не является безопасной. Это так же, как и любое другое предварительное условие, которое вы не можете с пользой проверить: у них есть UB, если требование нарушено, и это ошибка вызывающего абонента.
Для сравнения, free()
вероятно, будет иметь похожее поведение (и, возможно, худшие побочные эффекты), если вы нарушите предварительное условие, что его аргумент указателя был возвращен из malloc()/realloc()
,
Это выглядит небезопасно, что, если память в адресе (pucFrame - 1) уже используется для другой переменной и перезапись может привести к сбоям
Это небезопасно - это C. Ответственность за его правильное использование лежит на вас. Если вы используете его неправильно (передав указатель, который не соответствует установленным требованиям), будут неполадки, и они будут вашей ошибкой.
Как вы думаете, можно ли использовать подобный код или его неправильный способ, и его никогда не следует использовать?
Он может быть использован безопасно, так же, как free()
можно безопасно использовать. Это может быть использовано неправильно так же, как free()
может быть неправильно использован. Единственная причина никогда не использовать подобный код в том, что вы не доверяете себе, чтобы использовать его правильно.
Практический совет, чтобы посмотреть, где это pucFrame
указатель приходит, убедитесь, что он всегда гарантированно соответствует требованию, а затем убедитесь, что вы ничего не нарушаете в пути указателя через ваш код.
Никогда не предполагайте, что что-то лежит до или после определенного адреса, если вы не проверяете что-либо в пределах массива.
MISRA-C: 2004, правило 17.4 (обязательно) или MISRA-C:2012, правило 18.4 (обязательно) Индексирование массива должно быть единственно допустимой формой арифметики указателей.
Настоятельно рекомендуется использовать статический анализатор, такой как PC-Lint, для проверки работоспособности вашего кода во избежание неопределенного поведения.
Кроме того, вы можете поместить все необходимые вещи в структуру и просто передать указатель на структуру функции. Но даже тогда не обращайтесь к членам структуры с помощью ++ или -, так как вы не будете иметь представления о добавленном заполнении, которое зависит от компилятора
Поведение заявления pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
будет неопределенным, если pucFrame
указатель на элемент в массиве или сразу после конца массива, и перед ним есть один элемент. Для этой цели скаляр можно рассматривать как массив длины один.
Это потому, что арифметика указателей действительна только в массивах.
Вы также отбрасываете const
, что может еще раз перевести программу в неопределенное состояние, в зависимости от того, что вы делаете с указателем (и правила для этого различаются в C и C++).
Проясните смысл вашего вопроса. В любом случае код немного небезопасен, но НЕ по той причине, по которой Вы говорите. Чтобы сделать его более надежным, вы должны проверить на нуль. Арифметика с указателями немного опасна, но я думаю, что этот старый код будет делать некоторые предположения в зависимости от контекста.
кажется, прибывает из:
FreeModbus Libary: переносимая реализация Modbus для Modbus ASCII/RTU. 3 * Copyright (c) 2006 Кристиан Уолтер
Так что да, код 2006 года может быть опасным, но, с другой стороны, он используется уже 15 лет... так что его можно использовать. (старый код обычно работает, но если вы не измените его..)