Объявление массивов перед «нормальными» переменными в c? - PullRequest
2 голосов
/ 25 марта 2010

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

foo(int a, int *b);

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

    foo(x, arr);

    return 0;
}

foo передается указатель в качестве второй переменной, которая иногда не указывает на массив arr . Мы проверяем это с помощью одного пошагового выполнения программы и видим, что значение переменной arr array-as-a-pointer в основной области видимости не совпадает со значением переменной-указателя 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. и поэтому мне было интересно, если он говорит, что массивы должны быть объявлены до переменных, не являющихся массивами?

Любой вклад в это будет приветствоваться.


Update

@ Адам Шимке и Томлогик:

Мне интересно, что 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 () после вызова прерывания отключения.

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

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

Ответы [ 7 ]

3 голосов
/ 25 марта 2010

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

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

3 голосов
/ 28 марта 2010

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

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

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

2 голосов
/ 26 марта 2010

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

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

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

2 голосов
/ 26 марта 2010

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

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

2 голосов
/ 25 марта 2010

Для C89 переменные должны быть объявлены в списке в начале области действия перед любым назначением. C99 позволяет смешивать присвоение декларации. Итак:

{ 
    int x; 
    int arr[5];

    x=5;
...

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

2 голосов
/ 25 марта 2010

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

1 голос
/ 25 марта 2010

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

Вы смотрели на разборку?

...