在Java中复制C struct padding

时间:2009-05-08 11:27:34

标签: java c compiler-construction padding

根据here,C编译器会在将结构写入二进制文件时填充值。正如链接中的示例所示,在编写这样的结构时:

struct {
 char c;
 int i;
} a;

到二进制文件,编译器通常会在char和int字段之间留下一个未命名的未使用的漏洞,以确保int字段正确对齐。

如何使用不同的语言(在我的例子中为Java)创建二进制输出文件(在C中生成)的精确副本?

是否有自动方式在Java输出中应用C填充?或者我是否必须通过编译器文档来了解它是如何工作的(顺便说一下,编译器是g ++)。

11 个答案:

答案 0 :(得分:14)

不要这样做,它很脆弱,会导致对齐和字节序错误。

对于外部数据,最好使用shift和掩码(而不是union!)来显式定义字节格式并编写显式函数以在内部和外部格式之间进行转换。

答案 1 :(得分:8)

这不仅适用于写入文件,也适用于内存。事实上,结构被填充在内存中,如果结构被逐字节写出,则会导致文件中出现填充。

一般来说很难确切地复制确切的填充方案,尽管我猜一些启发式方法会让你走得很远。如果你有结构声明,它有帮助进行分析。

通常,大于一个字符的字段将对齐,以便它们在结构内的起始偏移量是其大小的倍数。这意味着short s通常在偶数偏移上(可以被2整除,假设为sizeof (short) == 2),而double s将在可被8整除的偏移上,依此类推。

UPDATE :出于这样的原因(以及与endianness有关的原因)将整个结构转储到文件通常是个坏主意。最好逐个字段地进行,如下所示:

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

假设put - 函数只写入值所需的字节,这将向文件发出一个无填充版本的结构,解决了这个问题。通过相应地编写这些函数,也可以确保正确的,已知的字节排序。

答案 2 :(得分:5)

  

是否有自动应用C的方法   在Java输出中填充?或者我有   通过编译器文档   看它是如何工作的(编译器是   顺便说一句g ++。

都不是。相反,您明确指定数据/通信格式并实现该规范,而不是依赖于C编译器的实现细节。您甚至不会从不同的C编译器获得相同的输出。

答案 3 :(得分: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围绕你从文件中读取的某个字节数组。

如果它有用,我会在ByteBuffer上写更多内容。

答案 4 :(得分:2)

在Java中读取/编写C结构的一种方便方法是使用javolution Struct类(参见http://www.javolution.org)。这对于自动填充/对齐数据没有帮助,但它确实可以更方便地处理ByteBuffer中保存的原始数据。如果你不熟悉javolution,那么值得一看,因为那里还有很多很酷的东西。

答案 5 :(得分:1)

此孔是可配置的,编译器具有将结构对齐1/2/4/8字节的开关。

所以第一个问题是:您想要模拟哪种对齐方式?

答案 6 :(得分:1)

使用Java,数据类型的大小由语言规范定义。例如,byte类型是1个字节,short是2个字节,依此类推。这与C不同,其中每种类型的大小都取决于架构。

因此,了解如何格式化二进制文件以便能够将文件读入Java中非常重要。

可能需要采取措施以确保字段是特定大小,以解决编译器或体系结构中的差异。提及对齐似乎表明输出文件将取决于架构。

答案 7 :(得分:1)

您可以尝试preon

  

Preon是一个java库,用于构建一个用于比特流压缩数据的编解码器   声明的(基于注释的)方式。想想JAXB或Hibernate,但接下来是二进制   编码数据。

它可以处理大/小端二进制数据,对齐(填充)和其他功能的各种数字类型。这是一个非常好的图书馆,我非常喜欢

我的0.02美元

答案 8 :(得分:1)

我强烈推荐protocol buffers来解决这个问题。

答案 9 :(得分: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;
}

答案 10 :(得分:-1)

您可以更改c侧的打包以确保不使用填充,或者您可以在十六进制编辑器中查看生成的文件格式,以允许您在Java中编写一个忽略填充字节的解析器。