2

現在、msp430 MCU 用のアプリケーションを開発しており、奇妙な問題が発生しています。「通常の」変数の宣言後にスコープ内で配列を宣言すると、未定義のように見える動作が発生することがあることがわかりました。このような:

foo(int a, int *b);

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

    foo(x, arr);

    return 0;
}

foo には 2 番目の変数としてポインターが渡されますが、これはarr配列を指していないことがあります。プログラムを 1 ステップ実行してこれを確認し、メイン スコープの arr array-as-a-pointer 変数の値が foo スコープの b ポインター変数の値と同じではないことを確認します。いいえ、これは実際には再現可能ではありません。たまにこの動作を確認しただけです。

これは、foo 関数の 1 行が実行される前でも観察できます。渡されたポインター パラメーター (b) は単に arr のアドレスを指していません。

次のように、例を変更すると問題が解決するようです。

foo(int a, int *b);

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

    foo(x, arr);

    return 0;
}

なぜ私たちがこの行動を経験するのかについて、誰かが何か意見やヒントを持っていますか? それとも似たような経験?MSP430 プログラミング ガイドでは、コードが ANSI C89 仕様に準拠する必要があることを指定しています。それで、配列は非配列変数の前に宣言する必要があると言っているのだろうかと思っていましたか?

これに関するご意見をいただければ幸いです。


アップデート

@Adam Shiemke と 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() ステートメントを追加することです。

誰かが興味を持っている場合は、問題を再現する完全な例と修正を投稿できますか?

これに関するすべての入力に感謝します。

4

7 に答える 7

3

これはコンパイラのバグのようです。

最初の例(問題のある例)を使用し、関数呼び出しをとして記述foo(x, &arr[0]);した場合、同じ結果が表示されますか?配列を次のように初期化するとどうなりますint arr[5] = {0};か?これらはどちらも何も変更しないはずですが、変更するとコンパイラのバグが示唆されます。

于 2010-03-25T16:28:08.120 に答える
3

あなたの更新された質問で:

foo基本的に、関数を呼び出す前 ( ) および変数の宣言後に割り込みを無効にします。簡単な例で問題を再現することができました。解決策は_NOP()、無効化割り込み呼び出しの後にステートメントを追加することです。

組み込み/関数/マクロを無効にする割り込み(または割り込みが無効になっている)が、命令を「スキップ」するか何かを引き起こしているように聞こえます。コード化されているか、正しく機能しているかどうかを調査します。

于 2010-03-28T18:09:55.910 に答える
2

実際のコードがはるかに複雑であると仮定すると、推測であることを覚えておいてください。

時々、スタックをオーバーフローしていませんか? もしそうなら、これはコンパイラ/uCによる「スタック防御」の何らかの成果物でしょうか? &foo の誤った値は、予測可能なメモリ範囲内にありますか? もしそうなら、その範囲には何らかの意味がありますか(スタック内など)?

mcu430 では、RAM と ROM のアドレス指定の範囲が異なりますか? つまり、RAM のアドレス空間は 16 ビットですが、プログラムのアドレス空間は 24 ビットですか? たとえば、PICにはそのようなアーキテクチャがあります。その場合、arr が rom (24 ビット) として割り当てられ、関数が ram (16 ビット) へのポインターを期待している可能性がある場合、コードは、arr がアドレス空間の最初の 16 ビットに割り当てられたときに機能しますが、その範囲を超えるとブリックになります。 .

于 2010-03-26T18:47:38.947 に答える
2

どちらの例も C89 に準拠しているように見えます。foo配列の境界を超えてアクセスしていないと仮定すると、動作に目に見える違いはないはずです。

于 2010-03-25T11:38:29.037 に答える
2

C89 の場合、割り当ての前にスコープの開始時に変数をリストで宣言する必要があります。C99 では、代入と宣言を混在させることができます。そう:

{ 
    int x; 
    int arr[5];

    x=5;
...

正当な c89 スタイルです。c99 をサポートしていない場合、コンパイラが何らかのエラーをスローしなかったことに驚きました。

于 2010-03-25T16:13:37.930 に答える
2

生成されたアセンブリ コードに基づいて、コンパイラのバグであるかどうかを判断できるはずです。変数宣言の順番を変えるとアセンブルが違う?デバッガーで許可されている場合は、アセンブリをシングル ステップで実行してみてください。

コンパイラのバグが見つかった場合は、最適化も確認してください。オプティマイザーによって導入されたこのようなバグを見てきました。

于 2010-03-25T21:59:46.893 に答える
1

プログラムのどこかで、スタックを破壊する不正なメモリ書き込みを行っている可能性があります。

分解の様子はご覧になりましたか?

于 2010-03-25T12:47:13.823 に答える