Могу ли я потребовать, чтобы возвращаемое значение было указателем на константу

Скажем, у меня есть два "модуля". Например, уровень аппаратного интерфейса порта RS-232 и уровень над ним, чтобы сделать его более абстрактным.

У меня есть приемный буфер, как это: U8BIT myBuffer[MAX], U8BIT typedeffed: typedef unsigned char

Однако есть два типа сообщений, которые могут быть получены. Тот, у которого есть заголовок, и другой, у которого нет заголовка. Логика для этого уже написана.

"Уровень выше" будет обращаться к этому буферу, но он не должен знать, является ли это заголовком или сообщением без заголовка.

Таким образом, у меня есть такая функция:

U8BIT * fooBuffer(U8BIT * maxLength)
{
    U8BIT * retval;
    if( isHeaderless() )
    {
        retval = &(myBuffer[0]);
        *maxLength = MAX;
    }
    else
    {
        retval = &(myBuffer[5]);
        *maxLength = MAX - 5;
    }
    return retval;
}

Как я могу убедиться, что любая функция, вызывающая эту функцию, не может изменить содержимое возвращаемого указателя?

Да, я знаю, что это всегда будет возможно. И не пытаясь усложнить другим попытаться изменить его. Я хочу, чтобы это было "невозможно", чтобы было легче совершать ошибки, потому что компилятор будет жаловаться, если вы попытаетесь изменить const,

Могу ли я просто объявить функцию следующим образом: const U8BIT * fooBuffer(U8BIT * maxLength)

4 ответа

Решение

Использование const U8BIT * в качестве типа возврата функции.

Например, в вашей функции:

const U8BIT * fooBuffer(U8BIT * maxLength)
{
    U8BIT * retval;  

    // code

    return (const U8BIT *) retval;
} 

Если retvalуказатель не разыменовывается внутри вашего fooBuffer функция, объявите это также как const U8BIT * и тогда приведение в операторе возврата больше не требуется.

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

В вашем случае код, вероятно, выиграет от изменения дизайна. Вот мое предложение.

#define FIRST_INDEX       0u
#define SOME_OTHER_INDEX  5u
#define N                 SOMETHING

static const uint8_t MAX = something;
static uint8_t myBuffer [N];


uint8_t fooBuffer (const uint8_t* retVal)
{
    uint8_t maxLength;

    if( isHeaderless() )
    {
        retval = &myBuffer[FIRST_INDEX];
        maxLength = MAX;
    }
    else
    {
        retval = &myBuffer[SOME_OTHER_INDEX];
        maxLength = (uint8_t) (MAX - SOME_OTHER_INDEX);
    }

    return maxLength;
}

Изменения в дизайне программы:

  • Гарантировано, что myBuffer является частной переменной, к которой нельзя получить доступ или изменить за пределами вашего модуля кода.
  • Возвращает maxLength по значению вместо указателя. Я не понимаю, почему вы должны вернуть его через указатель.
  • retVal возвращается через константный указатель.

Изменения в стиле кодирования:

  • Удалил тип U8BIT и заменил его на C99/C11-совместимый uint8_t. Если у вас нет компилятора C99/C11, просто typedef unsigned char uint8_t где-то. Это облегчает чтение вашего кода другими программистами. Если вы используете свои собственные специальные типы, они могут неправильно начать подозревать, что в этом конкретном типе есть что-то волшебное.
  • Удалена неясная скобка вокруг &myBuffer[], так как она не имеет смысла.
  • Убраны магические числа.

Соответствие MISRA-C:2004: (поскольку вы пометили этот MISRA)

  • u суффикс необходим для каждого целочисленного константа литерала.
  • Вы должны типизировать результат - оператор базового типа, uint8_t.

Как я могу убедиться, что любая функция, вызывающая эту функцию, не может изменить содержимое возвращаемого указателя?

Вернуть указатель на const это укажет ваше намерение пользователям вашего кода.
Однако обратите внимание, что нет никакой гарантии, что они не смогут изменить его. Помните, что они могут, и если они это сделают, это будет неопределенным поведением.

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

Да, уже начать использовать const квалифицированное возвращаемое значение.

Но вы можете пойти дальше, вернуть значение в место, которое действительно нельзя изменить. Если я правильно вижу, у вас есть только два возможных возвращаемых значения:

... myBuffer... // supposing that this is known at compile time

static U8BIT const retval0 = &(myBuffer[0]);
static U8BIT const retval1 = &(myBuffer[5]);

U8BIT const* fooBuffer(U8BIT * maxLength)
{
    if(isHeaderless()) return &retval0;
    else return &retval1;
}

Ваша платформа может даже иметь средства для обеспечения того, чтобы два retval в конечном итоге в разделе только для чтения.

Другие вопросы по тегам