Сохранить указатель на массив в области файла из параметра массива в функции

Я перестраиваю приложение для соответствия правилам MISRA и использую QA-C для анализа своего кода.

Одно из этих раздражающих правил касается указателей и массивов. Вы не можете сказать:

char foo[10];
char * bar = &(foo[0]);

bar[5] = 'a';

Вы также не можете сделать:

*bar = 'a';
bar++;

Моя проблема связана с двумя функциями и переменной файловой области.

Первоначально этот код делал следующее (подобно бит-псевдокоду):

static char * bufferPtr;

static void foo_a(char * buffer /* other params */)
{
    bufferPtr = buffer;

    /* some logic goes here */
    switch()
    {
        case 'b':
           foo_b(/* some param */);
}

static void foo_b(/* parameters */)
{
    if (/*something*/)
    {
        /*  some logic */
        foo_c(/* data-param */);
        /* some logic */
    }
    else
    {
        /*  some logic */
        foo_c(/* data-param */);
        /* some logic */
    }
}

static void foo_c(char data)
{
    *buffer++ = data;
}

Я попытался переписать его следующим образом:

static char (*bufferPtr)[];
static char bufferIndex;

static void foo_a(char buffer[] /* other params */)
{
    bufferPtr = &buffer;
    bufferIndex = 0;

    /* same */
}

static void foo_b(/* parameters */)
{
    /* same */
}

static void foo_c(char data)
{
    (*bufferPtr)[bufferIndex] = data;
    bufferIndex++;
}

Но потом и мисра, и мой компилятор (softune, Fujitsu) жалуются. Компилятор говорит:

присваивание несовместимых типов указателей из CHAR **' toCHAR (*)[]': оператор `='

Мишра говорит:

[C] Правый операнд присваивания не имеет совместимого типа указателя. Адрес автоматического объекта, экспортируемого в указатель со связью или более широкой областью действия.

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

Если я сделаю следующее в том же файле:

static CHAR foo[10];
static CHAR (*bar)[];
static void f(void)
{
    bar = &foo;
}

Тогда ни Мишра, ни мой компилятор ни на что не жалуются.


person Daan Timmer    schedule 03.07.2012    source источник
comment
Ему не нравится вот эта строчка: bufferPtr = поэтому нельзя static char **bufferPtr; ?   -  person SpacedMonkey    schedule 03.07.2012
comment
@SpacedMonkey, к сожалению, невозможно, потому что вы не можете/не должны использовать индексные указатели, как если бы они были массивами.   -  person Daan Timmer    schedule 03.07.2012
comment
В VS2010 буфер представляет собой char * внутри foo_a(), поэтому я не вижу решения. Вам нужно будет найти немного кода, совместимого с MISRA, который уже делает это.   -  person SpacedMonkey    schedule 03.07.2012
comment
@DaanTimmer Вы можете сделать это, это одно из предполагаемых применений указателей. MISRA говорит, что вы не должны?   -  person Daniel Fischer    schedule 03.07.2012
comment
@SpacedMonkey Не только в VS2010 массивы в качестве аргументов функций распадаются на указатели на первый элемент. Это часть спецификации.   -  person Daniel Fischer    schedule 03.07.2012
comment
@DanielFischer, но это верно: void foo(char buf[]){buf[i] = 'f';i++;}, а это не так: void foo(char * buf){buf[i] = 'f'; i++} или: void foo(char * buf){*buf = 'f'; buf++;}   -  person Daan Timmer    schedule 03.07.2012
comment
Кто сказал, что первое верно, а второе нет? Они совершенно одинаковы.   -  person Daniel Fischer    schedule 03.07.2012
comment
почему void foo(char * buf){*buf = 'f'; buf++;} недействителен?   -  person Agent_L    schedule 03.07.2012
comment
Правило 17.4 Мишры говорит об этом. К сожалению, это не то, что я придумал. Вот обсуждение этого вопроса: misra.org.uk/forum /viewtopic.php?f=73&t=641   -  person Daan Timmer    schedule 03.07.2012
comment
Нет. char * buf не является типом массива, а *buf = 'f' не применяет индексацию.   -  person Agent_L    schedule 03.07.2012
comment
@Agent_L, но buf++; - это арифметика указателя. Что не допускается согласно правилу 17.4.   -  person Daan Timmer    schedule 03.07.2012
comment
Я думал, что это МИСРА. Чем больше я узнаю об их правилах, тем меньше в них смысла. Как уже говорилось, первые два точно такие же.   -  person Daniel Fischer    schedule 03.07.2012
comment
@DaanTimmer Как мы можем соблюдать правило 17.4, если мы этого не знаем? Извините, вам нужно обратиться к кому-то, у кого есть доступ к правилам, чтобы получить дополнительную помощь.   -  person Agent_L    schedule 03.07.2012
comment
@DanielFischer та же проблема. :-) Но правила есть правила. Думаю, я просто изменю поток функциональности, где я продолжаю передавать буферный массив каждой последующей функции. Потому что А) это безопасно, Б) компилятор не жалуется, В) мисра не жалуется. Хотя это уродливый обходной путь.   -  person Daan Timmer    schedule 03.07.2012
comment
@DanielFischer Похоже, они предназначены для того, чтобы держать под контролем небрежных программистов. Это может быть хорошо при проектировании с нуля, но кажется, что адаптация существующего программного обеспечения — это в лучшем случае кошмар.   -  person Agent_L    schedule 03.07.2012
comment
@Agent_L мы переписываем 95% кода. Но в данном случае это был источник esp_printf: fizzy.biz/ ~phillip/programming_notes/embedded_c/ Нам пришлось изменить это, чтобы соответствовать мисре. Мы не можем использовать vsprintf из stdio.h, потому что misra жалуется на использование stdio.h из-за неопределенного поведения, и его следует избегать в производственном коде...   -  person Daan Timmer    schedule 03.07.2012
comment
@DaanTimmer С другой стороны, глобальные переменные тоже многими считаются плохими, поэтому явная передача буферного массива также имеет свои преимущества. (И да, если вы должны соблюдать, вы должны, но было бы неплохо, если бы правила имели смысл.)   -  person Daniel Fischer    schedule 03.07.2012
comment
@DanielFischer, но это не было глобальным. Это было в файловой, а не в глобальной области. Думаю, я просто отвечу на свой вопрос «этим» решением и оставлю его как есть. Теперь весь файл соответствует требованиям.   -  person Daan Timmer    schedule 03.07.2012
comment
@DaanTimmer - он по-прежнему глобальный и не реентерабельный. Неважно, за каким пространством имен или областью действия он скрывается. Точно так же, как статические члены класса также ничем не отличаются от глобальных.   -  person Agent_L    schedule 03.07.2012


Ответы (5)


Это не имеет для меня смысла:

static char (*bufferPtr)[];
static char bufferIndex;

Я бы сделал так:

static char *bufferPtr_new;
static int bufferIndex_new;

И замените каждый *bufferPtr на bufferPtr_new[bufferIndex_new] Но, возможно, объясните нам, что не так с текущим кодом, согласно MISRA.

person Agent_L    schedule 03.07.2012
comment
Согласно мисре, вы не можете использовать указатель, как если бы он был массивом: static char *ptr; static char index; ptr[index] = 'f'; приводит к следующему: оператор индекса массива используется для индексации выражения, которое не относится к типу массива. Это вполне законно в языке C, когда указатель обращается к элементу массива; но это может быть обескуражено некоторыми стандартами кодирования, которые стремятся ограничить использование указателей. - person Daan Timmer; 03.07.2012
comment
@DaanTimmer, так что пишите *(ptr + i) вместо ptr[i]. Удовлетворяет ли это MISRA? - person Daniel Fischer; 03.07.2012
comment
@DanielFischer, ха, я собирался сказать это, но старый код никогда не использовал индексирование... - person Agent_L; 03.07.2012
comment
Следует избегать арифметики указателя @DanielFischer. ptr + i — это такая метрика воздуха, которой следует избегать. (Если возможно. В настоящее время мы пытаемся удалить все это). Если нет реального решения) - person Daan Timmer; 03.07.2012
comment
@Agent_L В примере перед кодом, вместе с комментарием здесь, я предположил, что фактическая индексация была удалена во время извлечения кода для вопроса. - person Daniel Fischer; 03.07.2012
comment
@DaanTimmer [вставьте выбранный сильный язык] *(ptr + i) по стандарту точно эквивалентно ptr[i]. Пользоваться одним, но осуждать другое смешно. Единственная проблема заключается в том, что используются только разумные значения i, но это касается всех форм доступа. - person Daniel Fischer; 03.07.2012
comment
Арифметика указателя @DaanTimmer является совершенно законной конструкцией, в то время как индексирование чего-то, что не является массивом, является злоупотреблением языком (и арифметика все еще выполняется). Тем не менее, вы пытаетесь заменить первое вторым, чтобы выполнить требования, которые говорят об обратном. - person Agent_L; 03.07.2012
comment
@DanielFischer Хорошо, но представьте следующее. Массив изменяется с 8-битного на 16-битный. Но мы все еще увеличиваем i на +1. Вы получите ошибку выравнивания при использовании *(ptr+i), но не при использовании ptr[i]. - person Daan Timmer; 03.07.2012
comment
@Agent_L исходный источник также является массивом, а не указателем. - person Daan Timmer; 03.07.2012
comment
@DaanTimmer Приращение указывается в единицах sizeof *ptr, а не в байтах или октетах. Если у вас uint32_t *ptr;, то (char*)(ptr + 1) == ((char*)ptr) + 4, если CHAR_BIT == 8. - person Daniel Fischer; 03.07.2012
comment
@DaanTimmer Нет, даже если вы перейдете с символов на 49-байтовые структуры, ptr+1 по-прежнему означает один объект. - person Agent_L; 03.07.2012

Не уверен, что вам понравится это:

static char *(bufferPtr[10]); /* must have a size */
static char bufferIndex;

static void foo_a(char buffer[])
{
    *bufferPtr = buffer;
    bufferIndex = 0;
    /* etc */ 
}

Надеюсь, поможет.

person SpacedMonkey    schedule 03.07.2012
comment
Это то же самое, что и static char * bufferPtr[10], который представляет собой массив указателей, а не указатель на массив. - person Daan Timmer; 03.07.2012

static void foo_a(char * buffer /* other params */)

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

Было бы разумно, чтобы foo_a принимала указатель на массив (char (*buffer)[N] для некоторого N) или указатель на struct, содержащий массив соответствующего размера. Затем вы можете назначить файлу static char (*bufferPtr)[N] или (лучше) передать указатель массива через цепочку вызовов.

person ecatmur    schedule 03.07.2012
comment
Я также пробовал это. но затем он жаловался в другом месте, что он преобразовывал char (*) [] в char **. Хотя спасибо за ответ - person Daan Timmer; 03.07.2012
comment
@DaanTimmer хорошо, тогда исправьте код, который выполняет это преобразование. - person ecatmur; 03.07.2012

Одно из этих раздражающих правил касается указателей и массивов. Нельзя говорить: ....

char foo[10];

Совершенно нормально, за исключением типа char, который не разрешен.

char * bar = &(foo[0]);

Хорошо, если foo объявлен с синтаксисом массива. Если foo был объявлен с использованием синтаксиса указателя, это нарушает правило 17.4. См. исправление для правила 17.4 ниже. Кроме того, скобки не имеют никакого смысла ни для MISRA, ни по другим причинам.

bar[5] = 'a';

Прекрасно.

*bar = 'a';

Прекрасно.

bar++;

Это запрещено правилом 17.4, в котором говорится, что индексация массива — единственная разрешенная форма арифметики указателей. Это действительно очень глупое правило, и я думаю, что оно будет удалено в следующей версии MISRA. На самом деле я однажды обратился с этим правилом к ​​комитету, и они не смогли объяснить это правило, кроме косметики.

Решение состоит в том, чтобы создать отклонение от MISRA-C для этого правила, что, по-видимому, делает большинство реализаций MISRA. Не просто слепо следуйте MISRA. 90% правил хороши, но есть и странные, которым не хватает обоснования.

person Lundin    schedule 04.07.2012

Я изменил поток этого кода, чтобы просто передавать параметр буфера в каждую последующую версию.

Это уродливо, но работает и в некоторой степени безопасно. 'согласно мисре'

person Daan Timmer    schedule 06.07.2012