不同平台上的二进制未格式化数据会不会有什么不同?

Could there be any difference of binary unformatted data on different platforms?

我正在做一个科学项目,有两个超级老程序代码A和代码B。这两个代码之间的交互是通过二进制无格式数据。数据排列为每四个字节组合成一个浮点数。这两个程序都是很多年前写的,开发人员现在在不同的机构,很难联系到。由于答辩急需看结果,而我的程序又不是很好,所以为了避免调试的麻烦,我使用了与开发者或mod作者相同的平台来编译和运行程序。

代码A主要用C语言编写,其余部分用bash和FORTRAN语言编写。目前还不清楚开发人员在哪个平台上编写了这个程序,但我使用的是去年毕业的学生传来的修改版本。该学生在 Windows 的 64 位 Cygwin 上修改了程序,我发现这是我唯一可以编译并且 运行 没有错误的平台。例如,如果我在 OSX、Ubuntu 或 32 位 Cygwin 上 运行 它,我将 运行 进入我可以处理的范围之外的错误,因为我的编程时间和技能有限。

代码 B 在 SunOS 上用 C 语言编写,并依赖于 Sun 性能库。同样,我只能在我们计算机实验室的一台古董 Solaris 机器上成功编译和 运行 它。在其他平台上我丢失了很多headers,要全部收集起来很麻烦。根据文档,代码 B 使用代码 A 的输出二进制数据,格式排列方式完全相同。如果我将输出数据从代码 A 复制到代码 B 并 运行 它,代码 B 会抱怨数据的有效性 /* 屏幕输出:检测到负值! */.尽管如此,代码 B 运行 如果我使用其目录中提供的样本数据没问题 - 开发人员用来编写和测试他的程序的数据。

首先,如果我尝试只采用代码B的数据读取功能,创建一个简单的C项目并在我的main()函数中调用它,我发现在我的IDE中我无法重现漏洞。原代码B中读取数据的算法是这样的:

int i, nxyz, fildes;
char  msg[1024];
nxyz = nx * ny * nz;
if ((fildes=open(vmfile, O_RDONLY, 0664)) <= 1)
{
    perror(vmfile);
    return 1;
}
if (read(fildes, vmodel, nxyz*sizeof(float)) < 0)
{
    sprintf(msg, "Reading %s[=10=]", vmfile);
    perror(msg);
    return 2;
}
/* checking validity codes go here */
close(fildes);

该函数正在正确读取我的二进制未格式化数据,但没有正确读取开发人员的数据。这与尝试在 Solaris 上编译项目时发生的情况相反。

其次,我尝试绕过代码B的数据读取功能,写一个新函数通过ASCII文件读取数据,并确保存储数据的数组正确返回给main()函数。进行此修改后,数据已正确加载,但出乎意料的是,加载数据后,程序立即停止并引发了分段错误,这在 运行 开发人员的示例数据中从未见过。我不确定我是应该继续解决这个新错误还是放弃这个想法。

那么,32 位和 64 位平台、Linux 和 Windows、Solaris 和其他 Linux 机器上的二进制未格式化数据之间是否存在任何差异?如果有,在这种紧急情况下,有没有什么方法可以在不修改源代码的情况下转换数据 /* 因为它一定比进行任何调试要快得多 */?

不同的平台很容易有不同的基础数据大小。

在 cygwin 上,32 位和 64 位数据大小已经存在差异 在指针上,long,long double。 尝试在所有不同平台之间比较类似此程序输出的内容:

$ cat  size_of.c
#include <stdint.h>
#include <stdio.h>
#include <math.h>

int
main()
{
  printf("sizeof(char) == %d\n", sizeof(char));
  printf("sizeof(short) == %d\n", sizeof(short));
  printf("sizeof(int) == %d\n", sizeof(int));
  printf("sizeof(long) == %d\n", sizeof(long));
  printf("sizeof(long long) == %d\n", sizeof(long long));
  printf("sizeof(long long int) == %d\n", sizeof(long long int ));
  printf("sizeof(float) == %d\n", sizeof(float));
  printf("sizeof(double) == %d\n", sizeof(double));
  printf("sizeof(long double) == %d\n", sizeof(long double));
  printf("sizeof(wchar_t) == %d\n", sizeof(wchar_t));
  printf("sizeof(u_long) == %d\n", sizeof(u_long));
  printf("sizeof(ssize_t) == %d\n", sizeof(ssize_t));
  printf("sizeof(size_t) == %d\n", sizeof(size_t));
  void *p;
  printf("sizeof(*void) == %d\n", sizeof(p));

  return 0;
}

如果 Solaris 机器是 SPARC,那么除非程序小心地按照指定的顺序格式化数据,否则大于一个字节的数字将以不同于 Intel CPU 使用的顺序存储正如您在 Windows 机器上找到的那样。这叫做endianness.

浮点数也可以写成多种格式,但是 IEEE floating point standard is probably what's been used here. Of course there's a lot more to know about how floating point numbers are handled 如果您想了解此类数据的限制和可能出现的错误。