参照によって渡されるパラメーターが関数内で変更されないのはなぜですか?
-
02-07-2019 - |
質問
静的ライブラリにC関数があります。次のインターフェイスを使用して、Aと呼びましょう。
int A(unsigned int a, unsigned long long b, unsigned int *y, unsigned char *z);
この関数はyとzの値を変更します(これは確かです)。 extern" C"を使用して、ダイナミックC ++ライブラリ内から使用します。
さて、これが私を苦しめるものです:
- yは適切に設定され、zは変更されません。正確に言うと、両方が666の(ポイントされた)値で初期化された場合、yが指す値は呼び出し後に変更されますが、zが指す値は変わりません(まだ666)。
- Cバイナリから呼び出された場合、この関数はシームレスに動作します(値 zが指すものは変更されます)。
- 同じプロトタイプを持つ関数を使用してダミーCライブラリを作成し、それを動的C ++ライブラリ内から使用すると、非常にうまく機能します。同じ変数を再利用してA(..)を呼び出すと、以前と同じ結果が得られます。zは変更されません。
上記の点は、変数の宣言の愚かな間違いではないことを示していると思います。
明らかに立ち往生しており、Cライブラリを変更できません。何が問題になるのか、手がかりはありますか? インスタンスごとにchar *が解釈される方法について、C / C ++インターフェイスの問題について考えていました。
編集:問題が何であるかがようやくわかりました。以下の回答をご覧ください。
解決 9
まず、皆様のご協力に感謝いたします。 あなたが私に与えた多くのアイデアと手がかりのおかげで、私はついにこの問題を解決することができました。あなたのアドバイスは、私が当たり前だと思ったことに疑問を投げかけるのに役立ちました。
私の問題に対する簡単な答え:問題は、C ++ライブラリが古いバージョンのCライブラリを使用していたことです。この古いバージョンは、4番目の引数を逃しました。結果として、4番目の議論は明らかに変更されませんでした。
これが問題であることに気付いた今、少し恥ずかしく思います。ただし、コードが正常にコンパイルされているという事実に誤解されていました。これは、C ++ライブラリが正しいバージョンのC libに対してコンパイルされたが、実行時に、使用している別のライブラリと静的にリンクされた古いバージョンを使用したためです。
C++ Lib (M) ---> dyn C++ lib (N) ---> C lib (P) v.1.0
|
------> C lib (P) v.1.1
(N)は、(P)バージョン1.0と静的にリンクされている動的ライブラリです。 コンパイラは、(P)バージョン1.1にリンクしているため、(M)から4つの引数を持つ関数への呼び出しを受け入れましたが、実行時には古いバージョンの(P)を使用しました。
この回答や質問を自由に編集したり、編集を依頼したりしてください。
他のヒント
CライブラリとC ++コンパイラが long longs を処理する方法の違いのように見えます。私の推測では、CライブラリはおそらくC89以前の標準であり、実際には64ビットの long long を32ビットのlongとして扱っているということです。 C ++ライブラリはそれを正しく処理し、64ビットを呼び出しスタックに配置するため、yとzが破損します。 * int A(unsigned int a、unsigned long b、unsigned int * y、unsigned char z)を使用して関数を呼び出して、何が得られるか確認してください。
考えてみてください。
これは、あなたが説明したことから明らかに悪いことは何もないが、期待どおりに機能しない問題の1つです。
投稿を編集して、適切な回答を得るために、より多くの情報を提供する必要があると思います。特に、始めましょう:-
- このコードの対象プラットフォーム: Windows、Linux、組み込みのもの または...?
- Cコンパイラとは で構築された静的ライブラリ?
- 何 コンパイラはC ++動的ライブラリです で構築?
- Cコンパイラとは 正常に呼び出すことができます で構築されたライブラリ?
- はありますか ソースレベルのデバッガー?もしそうなら、できます Cコードをステップインする C ++。
AがZが指すデータを常に変更することについて間違っていない限り、問題を引き起こす可能性のある唯一の原因は、パラメーターの受け渡し規則間の非互換性です。 「long long」問題は、物事が見た目通りではないというヒントかもしれません。
最後の手段として、逆アセンブルされたC ++呼び出しコード(失敗と言う)とC呼び出しコード(成功と言う)を比較するか、デバッガーでCPU命令をステップスルーする(はい、本当に-あなた)良いスキルを学び、問題を解決します)
私が知る限り、long longは標準C ++の一部ではなく、おそらくそれが問題の原因です。
ダンノ。 Aにデバッグステップして、何が起こるかを確認してみてください(アセンブリコードアラート!)
C ++ライブラリから呼び出すCライブラリに元の関数をラップできますか?
ポイント2と3に基づいて、これはうまくいくようです。
もしそうでなければ、より多くの手がかりを見つけるための別のデバッグポイントを提供します-障害が最初に現れるライブラリを確認し、2と3が機能する理由を確認しますが、これは機能しません-最小限のものは何ですか違い?
それぞれの場合に関数呼び出しによって設定されたスタックを調べて、違いがここにあるかどうかを確認することもできます。異なる呼び出し規約を考慮してください。
ステップ1:C ++側から渡されたポインターyとzを、C関数が受け取ったポインターと比較します。
PS明白に聞こえたくはありませんが、ここで再確認してください。 Cバイナリから呼び出されたときにzが正常に変更されたと言う場合、zが指しているデータが正常に変更されたということです。ポインターyおよびz自体は値によって渡されるため、ポインターを変更することはできません。
もう一つのワイルドな推測:Cライブラリの関数の正しいインスタンスに対してリンクしていると確信していますか?あなたのライブラリで利用可能なそのような関数がいくつかあるのでしょうか? Cでは、リンカは関数の解決方法を決定するときに戻り値の型やパラメータリストを気にしません。名前だけが重要です。したがって、同じ名前の関数が複数ある場合...
プログラムで機能のアイデンティティを確認できます。いくつかのテストパラメーターを使用して関数Aを呼び出し、正常に動作し、関数Aへのポインターを出力するCライブラリを作成します。ライブラリをC ++アプリにリンクします。次に、C ++コードから見た元のA関数へのポインタを出力し、同じプロセスで呼び出されたときにCライブラリから見たポインタと比較します。
これも明らかですが、誰が知っていますか...呼び出しているC関数はステートレスであり、その出力はその入力のみに依存するということですか?関数がステートレスではない場合、「非表示」が状態は、C ++アプリから呼び出されたときの関数のさまざまな動作( z
が指すデータを変更しない)を担当します。
C ++プログラムでは、プロトタイプは extern" C"
で宣言されていますか?