测试C ++代码以实现字节序独立性

时间:2011-06-20 14:24:24

标签: c++ testing endianness

如何测试或检查C ++代码是否具有字节序独立性?它已经实现了,我只想验证它可以在小端和大端平台上运行。

我可以编写单元测试并在目标平台上运行它们,但我没有硬件。也许模拟器?

是否可以进行编译时间检查?

8 个答案:

答案 0 :(得分:15)

如果您可以访问基于x86的Mac,那么您可以利用Mac OS X内置PowerPC仿真以及x86(小端)和PowerPC(大端)的开发人员工具支持这一事实。这使您可以在同一平台上编译和运行大端和小端可执行文件,例如

$ gcc -arch i386 foo.c -o foo_x86 # build little endian x86 executable
$ gcc -arch ppc foo.c -o foo_ppc  # build big endian PowerPC executable

在构建了big endian和little endian可执行文件后,您可以运行任何可用的单元测试,这将捕获一些与字节序相关的问题,并且您还可以比较可执行文件生成的任何数据(文件,网络数据包,无论如何) - 这显然应该匹配。

答案 1 :(得分:10)

您可以使用qemu以相反的字节顺序设置执行环境。例如,如果您可以访问little-endian amd64或i386硬件,则可以设置qemu来模拟PowerPC Linux平台,在那里运行代码。

答案 2 :(得分:7)

答案 3 :(得分:4)

我建议采用一种编码技术来避免这个问题。

首先,您必须了解在哪种情况下会出现字节序问题。然后要么找到与endianess无关的方式来编写它,要么隔离代码。

例如,可能出现字节序问题的典型问题是当您使用内存访问或联合来挑选更大值的部分时。具体而言,避免:

long x;
...
char second_byte = *(((char *)&x) + 1);

相反,写一下:

long x;
...
char second_byte = (char)(x >> 8)

连接,这是我的最爱之一,因为许多人倾向于认为你只能使用奇怪的技巧来做到这一点。不要这样做:

union uu
{
  long x;
  unsigned short s[2];
};
union uu u;
u.s[0] = low;
u.s[1] = high;
long res = u.x;       

而是写:

long res = (((unsigned long)high) << 16) | low

答案 4 :(得分:1)

  

我可以编写单元测试并在目标平台上运行它们,但我没有硬件。

您可以设置您的设计,使单元测试易于独立于实际拥有硬件而运行。您可以使用依赖注入来执行此操作。我可以通过提供我正在测试的代码与之对话的基本接口类来抽象出硬件接口之类的东西。

class IHw
{
public:
    virtual void SendMsg1(const char* msg, size_t size) = 0;
    virtual void RcvMsg2(Msg2Callback* callback) = 0;
     ...
};

然后我可以得到实际与硬件对话的具体实现:

class CHw : public IHw
{
public:
    void SendMsg1(const char* msg, size_t size);
    void RcvMsg2(Msg2Callback* callback);
};

我可以制作测试存根版本:

class CTestHw : public IHw
{
public:
    void SendMsg1(const char* msg, size_t);
    void RcvMsg2(Msg2Callback* callback);
};

然后我的真实代码可以使用具体的Hw,但我可以使用CTestHw在测试代码中模拟它。

class CSomeClassThatUsesHw
{
public:
   void MyCallback(const char* msg, size_t size)
   {
       // process msg 2
   }
   void DoSomethingToHw()
   {
       hw->SendMsg1();
       hw->RcvMsg2(&MyCallback);
   }
private:
    IHw* hw; 
}

答案 5 :(得分:0)

这只应该以机器特定的二进制格式保存整数数据。

编辑:

如何测试使用endianess的应用程序?基本上你不能(不动态)。该应用程序将适用于您提供的任何状态。您唯一可以测试的是持久数据的状态。在给定特定输入(给定的良好状态)的情况下,您可以验证持久化数据(输出)是否采用特定格式。

解决方案根本就是不这样做。

因此,任何保存二进制数据(到文件/网络流等)的点都要确保:

  • 不是二元
  • 或者如果它是二进制的,则使其与机器无关。

要使机器不可知,只需使用ntohl()和系列将整数转换为网络字节顺序和大小。然后你将永远保持一致。

答案 6 :(得分:0)

IMO,唯一接近正确的答案是马丁的。如果您不与二进制文件中的其他应用程序通信或读/写二进制文件,则无需解决字节顺序问题。如果所有持久数据都是字符流形式(例如,数据包是ASCII,输入文件是ASCII,输出文件是ASCII),小端机器中会发生什么,它会停留在小端机器上。

我这是一个答案,而不是对马丁答案的评论,因为我建议你考虑做一些与马丁提出的不同的事情。鉴于主流机器架构是小端,而网络顺序是大端,如果您可以完全避免字节交换,则会产生许多优势。解决方案是使您的应用程序能够处理错误的端序输入。使通信协议以某种机器标识包开始。有了这些信息,您的程序就可以知道它是否必须按字节交换后续传入的数据包或保持原样。如果二进制文件的标头有一些指示器可以让您确定这些文件的字节顺序,则适用相同的概念。有了这种架构,您的应用程序就可以以原生格式编写,并且可以知道如何处理非本机格式的输入。

好吧,差不多。二进制交换/二进制文件还有其他问题。一个这样的问题是浮点数据。 IEEE浮点标准没有说明如何存储浮点数据。它没有说明字节顺序,也没有说明有效数字是在指数之前还是之后,没有关于存储指数和有效数的存储位顺序。这意味着你可以拥有两个具有相同字节序的不同机器,它们都遵循IEEE标准,你仍然可以将浮点数据作为二进制文件进行通信。

另一个今天不那么普遍的问题是,字节序不是二元的。除了大与小之外,还有其他选择。幸运的是,以2143顺序(而不是1234或4321顺序)存储事物的计算机时代已经远远落后于我们,除非你处理嵌入式系统。

底线: 如果你正在处理一组近乎同质的计算机,只有一两个奇怪的计算机(但不是太奇怪),你可能想要避免网络秩序。如果域具有多个体系结构的机器,其中一些非常奇怪,您可能不得不求助于网络顺序的通用语言。 (但请注意,通用语并不能完全解决浮点问题。)

答案 7 :(得分:0)

我个人使用 Travis 来测试我托管在 github 上的软件,它支持在多种架构上运行 [1],包括大端的 s390x。

我只需将它添加到我的 .travis.yml 中:

arch:
  - amd64
  - s390x  # Big endian arch

这可能不是唯一提出此建议的 CI,但这是我已经在使用的 CI。我在两个系统上都运行单元测试和集成测试,这让我有一定的信心,无论字节序如何,它都能正常工作。

不过,这不是灵丹妙药,我也想有一个简单的方法来手动测试它,以确保没有隐藏的错误(例如,我使用的是 SDL,颜色可能是错误的。我正在使用屏幕截图来验证输出但截取屏幕截图的代码可能有错误补偿显示问题,因此测试可以通过,但显示错误)。

[1] https://blog.travis-ci.com/2019-11-12-multi-cpu-architecture-ibm-power-ibm-z