4

hereによると、C コンパイラは構造体をバイナリ ファイルに書き込むときに値をパディングします。リンクの例が言うように、次のような構造体を書くとき:

struct {
 char c;
 int i;
} a;

バイナリ ファイルに追加すると、コンパイラは通常、int フィールドが適切に配置されるように、char フィールドと int フィールドの間に名前のない未使用の穴を残します。

別の言語 (私の場合は Java) を使用して、バイナリ出力ファイル (C で生成) の正確なレプリカを作成するにはどうすればよいですか?

Java出力にCパディングを自動的に適用する方法はありますか? または、コンパイラのドキュメントを調べて、それがどのように機能するかを確認する必要がありますか (ちなみに、コンパイラは g++ です)。

4

11 に答える 11

14

これをしないでください。脆弱であり、アラインメントとエンディアンのバグにつながります。

外部データの場合は、バイト単位でフォーマットを明示的に定義し、シフトとマスク (union ではなく!) を使用して、内部フォーマットと外部フォーマットの間で変換する明示的な関数を作成する方がはるかに優れています。

于 2009-05-08T11:37:59.090 に答える
8

これは、ファイルへの書き込み時だけでなく、メモリ内でも当てはまります。構造体がメモリに埋め込まれているという事実は、構造体がバイト単位で書き出される場合、ファイルにパディングが表示されることにつながります。

一般に、正確なパディング スキームを確実に複製することは非常に困難ですが、ヒューリスティックによってはかなり遠くまで到達できると思います。構造体宣言があると、分析に役立ちます。

通常、1 文字より大きいフィールドは、構造内の開始オフセットがそのサイズの倍数になるように配置されます。これは、shorts が一般に偶数オフセット ( と仮定すると 2 で割り切れるsizeof (short) == 2) にあり、doubles が 8 で割り切れるオフセットにあることを意味します。

更新: このような理由 (およびエンディアンに関係する理由) により、構造体全体をファイルにダンプすることは一般的に悪い考えです。次のように、フィールドごとに実行することをお勧めします。

put_char(out, a.c);
put_int(out, a.i);

関数が値に必要なバイトのみを書き込むと仮定するとput、パディングのないバージョンの構造体がファイルに出力され、問題が解決します。これらの関数を適切に記述することで、既知の適切なバイト順序を確保することもできます。

于 2009-05-08T11:31:05.337 に答える
5

Java出力にCパディングを自動的に適用する方法はありますか? または、コンパイラのドキュメントを調べて、それがどのように機能するかを確認する必要がありますか (ちなみに、コンパイラは g++ です)。

ない。代わりに、C コンパイラの実装の詳細に依存するのではなく、データ/通信形式を明示的に指定し、その仕様を実装します。異なる C コンパイラから同じ出力を得ることさえできません。

于 2009-05-08T12:11:43.233 に答える
4

相互運用性については、ByteBuffer クラスを参照してください。

基本的に、特定のサイズのバッファーを作成し、異なる位置に異なる型の put() 変数を作成し、最後に array() を呼び出して「生の」データ表現を取得します。

ByteBuffer bb = ByteBuffer.allocate(8);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(0, someChar);
bb.put(4, someInteger);
byte[] rawBytes = bb.array();

しかし、どこにパディングを配置するか、つまり位置間でスキップするバイト数を決定するのはあなた次第です。

C から書き込まれたデータを読み取るには、通常、ファイルから読み取ったバイト配列の周りに ByteBuffer をwrap()します。

役に立つ場合は、ByteBufferについて詳しく書いています。

于 2009-05-08T12:13:12.343 に答える
2

Java で C 構造体を読み書きする便利な方法は、javolution Struct クラスを使用することです ( http://www.javolution.orgを参照)。これは、データの自動パディング/アラインメントには役立ちませんが、ByteBuffer に保持されている生データの操作がはるかに便利になります。ジャボリューションに慣れていない場合は、他にもたくさんのクールなものがあるので、一見の価値があります。

于 2009-05-08T19:03:11.950 に答える
1

あなたはプレオンを試すことができます:

Preonは、宣言的(注釈ベース)の方法でビットストリーム圧縮データのコーデックを構築するためのJavaライブラリです。JAXBまたはHibernateを考えてみてください。ただし、バイナリエンコードされたデータの場合です。

ビッグ/リトルエンディアンのバイナリデータ、アライメント(パディング)、およびその他の機能に沿ったさまざまな数値型を処理できます。とても素敵な図書館です。とても気に入っています。

私の0.02$

于 2009-05-08T12:52:30.813 に答える
1

まさにこの問題には、プロトコル バッファを強くお勧めします。

于 2009-05-08T19:09:11.093 に答える
1

この穴は構成可能で、コンパイラには構造体を 1/2/4/8 バイト単位で整列させるためのスイッチがあります。

したがって、最初の質問は次のとおりです。正確にどのアライメントをシミュレートしたいですか?

于 2009-05-08T11:30:09.380 に答える
1

Java では、データ型のサイズは言語仕様によって定義されています。たとえば、byte型は 1 バイト、short2 バイトなどです。これは、各型のサイズがアーキテクチャに依存する C とは異なります。

したがって、ファイルを Java に読み込むことができるようにするには、バイナリ ファイルがどのようにフォーマットされているかを知ることが重要です。

コンパイラまたはアーキテクチャの違いを考慮して、フィールドが特定のサイズであることを確認するための手順を実行する必要がある場合があります。アライメントについての言及は、出力ファイルがアーキテクチャに依存することを示唆しているようです。

于 2009-05-08T11:40:22.517 に答える
0

私が理解しているように、あなたはCプログラムの出力を制御していないと言っています。あなたは与えられたようにそれを取る必要があります。

では、特定の構造のセットについてこのファイルを読む必要がありますか、それとも一般的なケースでこれを解決する必要がありますか?つまり、誰かが「プログラムXで作成されたファイルは、Javaで読まなければならない」と言ったのでしょうか。それとも、JavaプログラムがCソースコードを読み取り、構造体定義を見つけて、それをJavaで読み取ることを期待していますか?

読み取る特定のファイルがある場合、問題はそれほど難しくありません。Cコンパイラの仕様を確認するか、サンプルファイルを調べて、パディングがどこにあるかを調べます。次に、Java側で、ファイルをバイトストリームとして読み取り、今後の値を作成します。基本的に、InputStreamから必要なバイト数を読み取り、それらを適切なデータ型に変換する一連の関数を記述します。好き:

int readInt(InputStream is,int len)
  throws PrematureEndOfDataException
{
  int n=0;
  while (len-->0)
  {
    int i=is.read();
    if (i==-1)
      throw new PrematureEndOfDataException();
    byte b=(byte) i;
    n=(n<<8)+b;
  }
  return n;
}
于 2009-05-08T17:29:51.253 に答える
-1

c 側のパッキングを変更して、パディングが使用されないようにするか、16 進エディターで結果のファイル形式を確認して、パディングされているバイトを無視するパーサーを Java で記述できるようにすることができます。

于 2009-05-08T11:31:27.253 に答える