0

私が使用しているコンパイラ (ARM 用の Codesourcery) には、va_arg() を壊すバグがあり、回避しようとしています。この場合、「ap」は、32 ビットおよび 64 ビット引数のリストへの単純なポインターです。コンパイラのバグは、va_arg() が壊れており、時々間違った値を返すことです。

va_list を任意の型のポインターにキャストし、それを使用してリストの値を取得できます。

void foo(va_list ap)
{
    int32_t  ival;
    double   dval;

    ival = *(int32_t*)≈
    dval = *(double*)&ap);
}

ただし、キャストタイプとしてインクリメント 'ap' をプリまたはポストするにはどうすればよいですか?

たとえば、これらの両方でエラーが発生します。

(int32_t*)&ap++;
++(int32_t*)&ap.

本当の「C」の達人が手を差し伸べてくれませんか? ユニオンを使用してポインターを操作するソリューションがありますが、もっと「C に値する」メソッドが必要です...

4

3 に答える 3

2

結局のところ、これはコンパイラの問題ではありません。この問題は、スタック ポインタが 8 バイト境界にないことが原因でした。これは、ロード スクリプトを変更して、スタックに 8 バイト アラインメントを使用することで修正されます。このプロジェクトは NutOS を使用していることを付け加えておきます。非常に便利なコードです。

ストック ロード スクリプトを使用すると、スタック ポインタが 8 バイト アラインされていない値でロードされました (常にではない場合もあります)。

私の ARM9 Linux プラットフォームにはこの問題はありませんが、パラメーターのマーシャリングと va_arg() コードは ARM7 コンパイラーと同じです。

関数が呼び出されると、コンパイラはスタック ポインターに 8 バイト アラインメントになると思われる値をロードすることに気付きました。これにより、r0 ~ r3 が 8 バイトでアラインされます。8 バイト アラインメントでは、va_arg() 演算が機能します。

これが私の質問に答えないことはわかっています。私は共用体を含む解決策を考え出しましたが、興味深いことに、余分なコードと思われるものが最適化されてしまいました。そのため、共用体を使用して va_list を操作しても、オーバーヘッドはそれほど増加しませんでした。

返信ありがとうございます。

于 2013-09-19T21:00:16.967 に答える
0

キャストも & 演算子も、割り当て可能な値 (等号の左側にある「L 値」とも呼ばれます) を返しません。あなたは試すことができます:

int ival;
void * pap = ≈
ival = *(int32_t*)pap;
pap += sizeof(int32_t);

ただし、前の回答で述べたように、va_list の実装はプラットフォームによって異なり、回避策は移植可能ではない可能性があります。回避策を講じる場合は、少なくとも va_list の単体テストを含めて、問題が発生するかどうかを確認してください。

于 2013-09-18T14:31:46.110 に答える
0

がポインターにキャスト可能であるという保証はありませんva_list(複数のレジスターまたはワードを含むコンパイラー マジックによって実装される場合があります)。

stdarg(3)に記載されているマクロ、つまりva_startva_endおよびva_arg(およびおそらく)のみを使用する必要がありますva_copy

あなたのコードは、定義上、移植性がなく、バグがあり、未定義の動作を引き起こします。

インクリメントapのみ使用va_arg(ap, int32_t)など...;

ところで、 GCCの新しいバージョン(つまり、2013 年 9 月 18 日の 4.8.1) をダウンロードして、ソース コードから (おそらくクロス コンパイラとして) コンパイルすることができます。あなたが持っているコンパイラのバグを解決したかもしれません。

于 2013-09-18T13:50:02.993 に答える