Вопрос

В настоящее время мы разрабатываем приложение для микроконтроллера msp430 и столкнулись с некоторыми странными проблемами.Мы обнаружили, что объявление массивов в области видимости после объявления «обычных» переменных, иногда вызывает то, что кажется неопределенным поведением.Так:

foo(int a, int *b);

int main(void)
{
    int x = 2;
    int arr[5];

    foo(x, arr);

    return 0;
}

foo передается указатель в качестве второй переменной, что иногда не указывает на обр. множество.Мы проверяем это, пройдя программу за один шаг, и видим, что значение переменной массива как указателя arr в основной области не совпадает со значением переменной указателя b в области foo.И нет, на самом деле это невозможно воспроизвести, мы просто время от времени наблюдали такое поведение.

Это заметно даже до того, как будет выполнена одна строка функции foo: переданный параметр-указатель (b) просто не указывает на адрес, которым является arr.

Изменение примера, похоже, решает проблему следующим образом:

foo(int a, int *b);

int main(void)
{
    int arr[5];
    int x = 2;

    foo(x, arr);

    return 0;
}

Есть ли у кого-нибудь какие-либо сведения или подсказки относительно того, почему мы испытываем такое поведение?Или похожий опыт?В руководстве по программированию MSP430 указано, что код должен соответствовать спецификации ANSI C89.и поэтому мне было интересно, говорит ли он, что массивы должны быть объявлены перед переменными, не являющимися массивами?

Любой вклад по этому поводу будет оценен по достоинству.


Обновлять

@Адам Шимке и tomlogic:

Мне интересно, что C89 указывает на различные способы инициализации значений внутри объявлений.Разрешено ли вам писать что-то вроде:

int bar(void)
{
    int x = 2;
    int y;

    foo(x);
}

И если да, то как насчет:

int bar(int z)
{
    int x = z;
    int y;

    foo(x);
}

Это разрешено?Я предполагаю, что следующее должно быть незаконным C89:

int bar(void)
{
    int x = baz();
    int y;

    foo(x);
}

Заранее спасибо.


Обновление 2Задача решена.По сути, мы отключаем прерывания перед вызовом функции (foo) и после объявления переменных.Мы смогли воспроизвести проблему на простом примере, и решение, похоже, состоит в том, чтобы добавить оператор _NOP() после вызова прерывания отключения.

Если кому-то интересно, я могу опубликовать полный пример, воспроизводящий проблему, и ее исправление?

Спасибо за все мнения по этому поводу.

Это было полезно?

Решение

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

Если вы обнаружите ошибку компилятора, проверьте оптимизацию.Я видел подобные ошибки, внесенные оптимизатором.

Другие советы

Это похоже на ошибку компилятора.

Если вы используете свой первый пример (проблемный) и записываете вызов функции как foo(x, &arr[0]);, вы видите те же результаты?А что, если вы инициализируете массив, например int arr[5] = {0};?Ни один из них не должен ничего изменить, но если они это сделают, это будет указывать на ошибку компилятора.

В вашем обновленном вопросе:

По сути, мы отключаем прерывания перед вызовом функции (foo) и после объявления переменных.Мы смогли воспроизвести проблему на простом примере, и решение, похоже, состоит в том, чтобы добавить _NOP() оператор после вызова прерывания отключения.

Звучит так, как будто прерывание, отключающее встроенное/функцию/макрос (или, однако, прерывания отключены), может вызывать «пропуск» инструкции или что-то в этом роде.Я бы проверил, правильно ли он закодирован/работает.

Оба примера кажутся мне соответствующими C89.Не должно быть заметной разницы в поведении, если предположить, что foo не имеет доступа за пределы массива.

В C89 переменные необходимо объявлять в списке в начале области видимости перед любым присвоением.C99 позволяет смешивать присваивание и объявление.Так:

{ 
    int x; 
    int arr[5];

    x=5;
...

является законным стилем c89.Я удивлен, что ваш компилятор не выдал какую-то ошибку, если он не поддерживает c99.

Предполагая, что реальный код намного сложнее, вот некоторые вещи, которые я бы проверил, имейте в виду, что это предположения:

Можете ли вы иногда переполнять стек?Если да, то может ли это быть каким-то артефактом «защиты стека» компилятора/uC?Попадает ли неверное значение &foo в предсказуемый диапазон памяти?если да, то имеет ли этот диапазон какое-либо значение (внутри стека и т. д.)?

Имеет ли mcu430 разные диапазоны адресации ОЗУ и ПЗУ?То есть адресное пространство оперативной памяти 16бит, а адресное пространство программы 24бит?Например, PIC имеет такую ​​архитектуру.Если это так, то вполне возможно, что arr будет выделен как rom (24 бита), и функция ожидает указатель на RAM (16 бит), код будет работать, когда arr будет выделен в первых 16 битах адресного пространства, но будет заблокирован, если он превышает этот диапазон. .

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

Разборку смотрел?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top