内容


定位您的应用程序 - 小端和大端 IBM XL C / C++ 编译器的差异对您意味着什么

Comments

简介

要从大端编译器迁移到小端编译器,您可能需要更改一些代码,以便维持程序的行为或结果。在将代码从大端移植到小端时,应该考虑矢量、不同大小的项目之间的存储关联、长双精度、复数和序列化等代码方面的差异。在 IBM Power Systems™ 上的小端 Linux® 版本使用了与大端版本不同的 ABI。此外,还需要更新对旧的 ABI 有依赖性的程序。新的内置函数使得针对矢量字节顺序的移植变得更容易。

本文将介绍在将用于 Power Systems 上的大端 IBM XL C / C ++ 的代码移植到 Power Systems 上的小端 IBM XL C / C ++ 时可能遇到的问题。本文对编码更改提出一些建议,还将介绍有助于移植代码的编译器特性和选项。

比较大端字节顺序和小端字节顺序

字节顺序决定了在内存中如何解释数据。平台的字节顺序由处理器的架构决定。目前使用的两种最常见的字节顺序类型是大端字节顺序和小端字节顺序。

在大端平台(在这篇文章中通常简称为大端)上,内存中的字节排序是最大的字节排在首位(或 “左边”)。在小端平台(在这篇文章中通常简称为小端)上,内存和矢量寄存器中的字节排序是最小的字节排在首位(或 “左边”)。

例如,图 1 描述了在大端和小端平台上的内存中如何存储 000102030405060716(被解释为一个 8 字节的整数)。在图 1 中,a 表示在该位置的内存地址。

图 1:内存中的大端和小端字节顺序的表示

矢量

IBM POWER® 处理器架构支持包含 16 个 1 字节元素、8 个 2 字节元素、4 个 4 字节元素或者两个 8 字节元素的 16 字节矢量。该处理器拥有 128 位的矢量寄存器,还提供了指令来将矢量加载到寄存器,操作寄存器中的矢量,并将矢量寄存器存储到内存。IBM XL 编译器提供了使用矢量的内置函数和语言支持。

在​​矢量元素顺序方面,大端和小端之间存在差异。为了帮助处理这些差异,我们还引入了新的编译器选项和内置函数。下面的章节中将会解释这些差异和新特性。

矢量元素顺序和矢量元素字节顺序

在矢量寄存器中布置矢量元素的方式有两种。您可以从低到高加载元素,所以元素 0 是矢量寄存器中最左边的元素。此外,您还可以从高到低加载元素,让元素 0 成为矢量寄存器中最右边的元素。前一种布置被称为大端矢量元素顺序,后一种布置被称为小端矢量元素顺序

在大端上,总是使用大端矢量元素顺序。

在小端上,您可以选择使用大端矢量元素顺序和小端矢量元素顺序。

不论采用哪一种矢量元素顺序,大端上的矢量元素都可以在内存中使用大端字节顺序。小端上的矢量元素默认情况下可以在内存中使用小端字节顺序。

为了说明大端和小端矢量元素顺序的差异,请参见图 2 和图 3。

图 2 说明了如何在矢量寄存器中用大端矢量元素顺序表示 000102030405060708090A0B0C0D0E0F16(被解释为一个 16 字节的矢量)。图上的 b127 b0 标记分别表示寄存器的第 127 位和第 0 位。图中从上至下显示了用 1、2、4 和 8 字节的元素填充的矢量的表示。

图 2:矢量寄存器中的大端矢量元素顺序表示

图 3 描述了如何在矢量寄存器中用小端矢量元素顺序表示 000102030405060708090A0B0C0D0E0F16(被解释为一个 16 字节的矢量)。图上的 b127 b0 标记分别表示寄存器的第 127 位和第 0 位。图中从上至下显示了用 1、2、4 和 8 字节的元素填充的矢量的表示。

图 3:矢量寄存器中的小端矢量元素顺序表示

-qaltivec 选项

-qaltivec 选项可用来告诉小端编译器如何在矢量寄存器中对矢量元素进行排序。

如果 -qaltivec=le,那么编译器将按照小端元素顺序来加载矢量,并假定按小端元素顺序将矢量加载到矢量存储。如果需要的话,编译器会插入矢量排列操作,以确保该加载和存储的内置函数使用的是小端元素顺序。对于引用特定元素的矢量内置函数,编译器假定是按小端元素顺序来加载矢量的。在小端上,-qaltivec=le 是默认值。

如果 -qaltivec=be,那么编译器将按照大端元素顺序来加载矢量,并假定按大端元素顺序将矢量加载到矢量存储。如果需要的话,编译器会插入矢量排列操作,以确保该加载和存储的内置函数使用的是大端元素顺序。对于引用特定元素的矢量内置函数,编译器假定是按大端元素顺序来加载矢量的。

为了说明这个问题,请考虑下面的程序:

#include<stdio.h>
union Example
{
 vector signed int vint;
 int sint[4];
};

int main() {
 union Example example;

 example.sint[0] = 0x0102;
 example.sint[1] = 0x0304;
 example.sint[2] = 0x0506;
 example.sint[3] = 0x0708;

 printf("First vector element:%04x\n", vec_extract(example.vint,0));
 printf("Second vector element:%04x\n", vec_extract(example.vint,1));
 printf("Third vector element:%04x\n", vec_extract(example.vint,2));
 printf("Fourth vector element:%04x\n", vec_extract(example.vint,3));

 return 0;
}

在大端平台上编译这个程序时,或者在小端平台上用 -qaltivec=le 编译它时,会产生以下输出:

First vector element:0102
Second vector element:0304
Third vector element:0506
Fourth vector element:0708

但是,如果在一个小端平台上使用 -qaltivec=be 编译该程序,则会产生以下输出:

First vector element:0708
Second vector element:0506
Third vector element:0304
Fourth vector element:0102

矢量被反向 加载到矢量寄存器中,而数组 i 中的元素顺序保持不变。

更简单的矢量使用方法是,使用下一节中介绍的 vec_xlvec_xl_bevec_xstvec_xst_be 内置函数来进行加载和存储。

请考虑下面的程序:

#include<stdio.h>

int main() {
 vector signed int v1;
 vector signed int v2;
 vector signed int v3;
 vector signed int v4;
 int a[4];

 v1 = vec_xl(0, (int[]){1,2,3,4});
 vec_xst(v1, 0, a);
 printf("v1 via a:%d %d %d %d\n", a[0],a[1],a[2],a[3]);

 v2 = vec_neg(v1);
 vec_xst(v2, 0, a);
 printf("v2 via a:%d %d %d %d\n", a[0],a[1],a[2],a[3]);

 //Merge high and low depend on vector element order
 v3 = vec_mergeh(v1, v2);
 vec_xst(v3, 0, a);
 printf("v3 via a:%d %d %d %d\n", a[0],a[1],a[2],a[3]);

 v4 = vec_mergel(v1, v2);
 vec_xst(v4, 0, a);
 printf("v4 via a:%d %d %d %d\n", a[0],a[1],a[2],a[3]);

 return 0;
}

该程序在大端平台和小端平台上产生了相同的输出。输出是:

v1 via a:1 2 3 4
v2 via a:-1 -2 -3 -4
v3 via a:1 -1 2 -2
v4 via a:3 -3 4 -4

矢量内置函数 vec_xlvec_xstvec_mergehvec_mergel 考虑了矢量元素顺序。换句话说,当在一个小端平台上用 -qaltivec=le 编译该程序时:

  • vec_xl 使用一个 Vector Scalar eXtension (VSX) 加载指令,该指令总是按大端元素顺序进行加载。然后,使用一个矢量排列指令来反转寄存器中的矢量,改用小端元素顺序。
  • vec_xst 假定在寄存器中的矢量使用了小端矢量元素顺序,所以它使用一个矢量排列指令,将矢量元素反转为大端矢量元素顺序。然后,它使用一个 VSX 存储指令将矢量存储回内存中,该指令总是按大端元素顺序进行存储。
  • vec_mergeh 知道矢量元素是从右侧开始的。矢量寄存器包含 v1v2,如下所示:
v1	 4	 3	 2	 1
v2	-4	-3	-2	-1

因为 vec_mergeh 是从右侧开始算起的,它正确地使用 1 和 2 作为 vec_mergeh(v1, v2) 的结果的元素 0 和 2。

  • vec_mergel 也同样知道矢量元素是从右侧开始的。因此,它正确地使用 -1 和 -2 作为 vec_mergel(v1, v2) 的结果的元素 1 和 3。

在大端平台或在小端平台上用 -qaltivec=be 编译该程序时:

  • vec_xl 使用了一个 VSX 加载指令,该指令总是按大端元素顺序进行加载。不需要矢量排列。
  • vec_xst 假定在寄存器中的矢量使用的是大端矢量元素顺序。因此,它直接使用一个 VSX 存储指令将矢量存储回内存中,该指令总是按大端元素顺序进行存储。
  • vec_mergeh 知道矢量元素是从左侧开始的。矢量寄存器包含 v1v2,如下所示:
v1	 1	 2	 3	 4
v2	-1	-2	-3	-4

因为 vec_mergeh 是从左侧开始算起的,所以它正确地使用 1 和 2 作为 vec_mergeh(v1, v2) 的结果的元素 0 和 2。

  • vec_mergel 也同样知道矢量元素是从左侧开始的。因此,它正确地使用 -1 和 -2 作为 vec_mergel(v1, v2) 的结果的元素 1 和 3。

对于没有使用联合的程序,可以使用 –qaltivec=be 选项将代码从大端移植到小端。

POWER8 加密内置函数要求其输入矢量采用大端矢量元素顺序。实现这一要求有两种方法,使用 -qaltivec=be,或者使用 vec_xl_bevec_xst_be 函数来加载和存储。这些矢量加载和存储函数将在下一部分中进行介绍。

新的矢量加载和存储内置函数

添加了新的矢量加载和存储内置函数,它们使用了 VSX 指令。请注意,面向小端 Linux on Power Systems 的 XL C/C++ 是一个 64 位编译器产品,所​​以只适用 64 模式的原型。在 XL C/C++ 编译器参考(XL C/C++ for Linux 文档库)中详述了这些内置函数。

vec_xl(offset, address)
这个函数从由位移 offsetaddress 的地址指定的内存地址加载一个 16 字节的矢量,使用与平台相应的元素顺序和 -qaltivec 选项。

原型(64 位模式):

vector signed char vec_xl(long, signed char *);
vector unsigned char vec_xl(long, unsigned char *);
vector signed short vec_xl(long, signed short *);
vector unsigned short vec_xl(long, unsigned short *);
vector signed int vec_xl(long, signed int *);
vector unsigned int vec_xl(long, unsigned int *);
vector signed long long vec_xl(long, signed long long *);
vector unsigned long long vec_xl(long, unsigned long long *);
vector float vec_xl(long, float *);
vector double vec_xl(long, double *);

vec_xl_be(offset, address)
这个函数从由位移 offsetaddress 的地址指定的内存地址加载一个 16 字节的矢量,无论在什么平台上,都使用大端顺序或 -qaltivec 选项。

原型(64 位模式):

vector signed char vec_xl_be(long, signed char *);
vector unsigned char vec_xl_be(long, unsigned char *);
vector signed short vec_xl_be(long, signed short *);
vector unsigned short vec_xl_be(long, unsigned short *);
vector signed int vec_xl_be(long, signed int *);
vector unsigned int vec_xl_be(long, unsigned int *);
vector signed long long vec_xl_be(long, signed long long *);
vector unsigned long long vec_xl_be(long, unsigned long long *);
vector float vec_xl_be(long, float *);
vector double vec_xl_be(long, double *);

vec_xst(vect, offset, address)
此函数将由 vect 指定的 16 字节矢量的元素存储到一个给定的内存地址中。该地址的计算方法是,将 offset 指定的位移添加到由 address 指定的内存地址,使用与平台相对应的元素顺序和 -qaltivec 选项。

原型(64 位模式):

void vec_xst(vector signed char, long, signed char *);
void vec_xst(vector unsigned char, long, unsigned char *);
void vec_xst(vector signed short, long, signed short *);
void vec_xst(vector unsigned short, long, unsigned short *);
void vec_xst(vector signed int, long, signed int *);
void vec_xst(vector unsigned int, long, unsigned int *);
void vec_xst(vector signed long long, long, signed long long *);
void vec_xst(vector unsigned long long, long, unsigned long long *);
void vec_xst(vector float, long, float *);
void vec_xst(vector double, long, double *);

vec_xst_be(vect, offset, address)
此函数将由 vect 指定的 16 字节矢量的元素存储到一个给定的内存地址中。该地址的计算方法是,将 offset 指定的位移添加到由 address 指定的内存地址,无论在什么平台上,都使用大端顺序或 -qaltivec 选项。

原型(64 位模式):

void vec_xst_be(vector signed char, long, signed char *);
void vec_xst_be(vector unsigned char, long, unsigned char *);
void vec_xst_be(vector signed short, long, signed short *);
void vec_xst_be(vector unsigned short, long, unsigned short *);
void vec_xst_be(vector signed int, long, signed int *);
void vec_xst_be(vector unsigned int, long, unsigned int *);
void vec_xst_be(vector signed long long, long, signed long long *);
void vec_xst_be(vector unsigned long long, long, unsigned long long *);
void vec_xst_be(vector float, long, float *);
void vec_xst_be(vector double, long, double *);

矢量文字和二进制编码的十进制 (BCD)

二进制编码的十进制 (BCD) 内置函数对在矢量寄存器中加载的带符号 BCD 值进行运算。每个 BCD 由一系列 4 字节半字节(nibbles)组成,其中含 0 至 9 中的一个值,并且最后一个半字节包含符号。例如,值 10 (在 4 字节半字节中)被表示为:

0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0

上述每个十六进制值表示一个 4 字节半字节。0x1 半字节代表 10 中的 1,其后的 0x0 半字节表示 10 中的0。0xC 半字节是一个特殊的值,它代表一个加号。因为没有 BCD 数据类型和 BCD 数字需要加载到矢量寄存器中,所以 BCD 内置函数使用了 vector unsigned char 参数和结果。这使得即使 BCD 数字本身并不是矢量,BCD 内置函数也要依赖于矢量元素顺序。换句话说,即使使用 –qaltivec=le 编译,也需要按大端矢量元素顺序加载含有 BCD 数字的矢量。当矢量被静态初始化(例如,使用矢量文字)时,这是一个问题:括号内含有按大端顺序列出的字节元素的静态矢量初始化表达式在小端平台上会按小端元素顺序填充矢量寄存器。

要解决这个问题,可以反转矢量初始化中的元素顺序。例如,请考虑下面的程序:

#include <stdio.h>
#if __LITTLE_ENDIAN__
#define BCD_INIT(b0, b1, b2, b3, b4, b5, b6, b7, \
                 b8, b9, ba, bb, bc, bd, be, bf) \
     { bf, be, bd, bc, bb, ba, b9, b8, b7, b6, b5, b4, b3, b2, b1, b0 }
#else
#define BCD_INIT(b0, b1, b2, b3, b4, b5, b6, b7, \
                 b8, b9, ba, bb, bc, bd, be, bf) \
     { b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf }
#endif

vector unsigned char v1001 =
  BCD_INIT(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
           0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1C);

vector unsigned char v9009 =
  BCD_INIT(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
           0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x9C);

static void print_bytes(vector unsigned char v)
{
  long i;
  unsigned char b[16];
  vec_xst_be(v, 0, b);
  printf("%.02hhx", b[0]);
  for (i = 1; i < 16; ++i)
  {
    printf(", %.02hhx", b[i]);
  }
  printf("\n");
}

int main(void)
{
  vector unsigned char result;
  printf("Adding statically initialized vectors\n");
  printf("op1 is    ");  print_bytes(v1001);
  printf("op2 is    ");  print_bytes(v9009);
  result = __bcdadd(v1001, v9009, 0);
  printf("result is ");  print_bytes(result);
  return 0;
}

该程序将在大端和小端平台上打印下面的数据。

Adding statically initialized vectors
op1 is    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 10, 00, 1c
op2 is    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 90, 00, 9c
result is 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 01, 0c

只在小端平台上预定义 __LITTLE_ENDIAN__ 宏。我们创建了一个 BCD_INIT 宏,以便反转初始化中的字节。请注意,print_bytes 函数使用了 vec_xst_be 内置函数,因为矢量是按大端矢量元素顺序进行排列的。

其他解决方法将在运行时使用 vec_xl_be 内置函数进行初始化,或在运行时使用 vec_reve 内置函数反转静态初始化的矢量的矢量元素顺序。

应用程序二进制接口 (ABI)

面向小端 Linux on Power Systems 的 XL C/C++ 使用了新的 IBM Power Architecture® 64 位 ELF V2 ABI 规范。这种新的 ABI 完善了几个方面,包括函数调用。然而,这意味着,针对旧 ABI 的程序集文件必须移植到新的 ABI。遵循各自的语言标准的 Fortran、C 和 C ++ 程序不需要移植到新的 ABI。包含非标准扩展的程序需要进行 ABI 敏感性审查。

不同大小的项目之间的存储关联

在将程序从大端移植到小端时,必须考虑不同大小的项目之间的存储关联。在 C / C ++ 中,这涉及到联合与指针转型。以下各节将会更详细地概述这些内容。

请注意,十六进制值始终按大端顺序进行打印。

联合

当使用不同大小的项目的联合时,在小端和大端平台上的结果可能会有所不同。为了证明这一点,请考虑下面的程序:

#include<stdio.h>

union Example
{
  char i;
  short j;
  int k;
};

main()
{
  union Example example;

  example.k = 0x01020304;

  printf("example.i:%02x\n", example.i);
  printf("example.j:%04x\n", example.j);
  printf("example.k:%08x\n", example.k);

  return 0;
}

在上述程序中,我们已指定 ijk 共享存储空间 – 它们的大小全都不同。在大端上,最大的字节放在最低的内存地址(或左侧)中,所以 ijk 包含以下数据:

example.i:01
example.j:0102
example.k:01020304

在小端上,最小的字节放在左侧,所以 ijk 包含以下数据:

example.i:04
example.j:0304
example.k:01020304

指针转型

指针转型的问题与那些由联合引入的问题其实是一样的。请考虑下面的程序:

#include<stdio.h>

int main() {
  int i = 0x01020304;  
  printf("example.i:%02x\n", *(char*)&i);
  printf("example.j:%04x\n", *(short*)&i);
  printf("example.k:%08x\n", *(int*)&i);
  return 0;
}

请注意,如果更改语言的规则,那么该代码将是无效(例如,不能将 integer 作为 short 来处理),不过,该代码简单地说明了这一问题。

在上面的程序中,指针转型被用于将 i 转型为 charshortint。在大端上,i 的最大字节在最低的内存地址中。该程序在大端上的输出如下所示:

example.i:01
example.j:0102
example.k:01020304

在小端上,i 的最小字节在最低的内存地址中。该程序在小端上的输出如下所示:

example.i:04
example.j:0304
example.k:01020304

长双精度和复数类型

XL C/C++ 的长双精度类型包含两部分双精度,它们使用不同的数量级,不会重叠(只有当数字是零或接近零时除外)。即使在小端上,高阶双精度(即首先进入存储的)也必须有较大的数量级。长双精度数字的值是它的两个实数部分的总和。

复数类型由一个实数部分和一个虚数部分组成,实数部分始终在虚数部分之前。在 C/C++ 中,您可以使用 __real____imag__ 一元运算符或 crealcimag 函数集来访问实数部分和虚数部分。

请考虑下面的程序:

#include<stdio.h>
#include<complex.h>

union Example
{
 float f[2];
 float complex c;
};

int main()
{
 union Example example;
 example.c = 1.0f + 0.0f*I;

 printf("First element of float:%.4f\n", example.f[0]);
 printf("Second element of float:%.4f\n", example.f[1]);
 printf("Real part of complex:%.4f\n", __real__(example.c));
 printf("Imaginary part of complex:%.4fi\n", __imag__(example.c));

 return 0;
}

在大端和小端上,f 的第一个元素都是 1.0000,f 的第二个元素都是 0.0000。复数 c 的实数部分是 1.0000,虚数部分是 0.0000i。

First element of float:1.0000
Second element of float:0.0000
Real part of complex:1.0000
Imaginary part of complex:0.0000i

浮点和复数的元素字节顺序在大端和小端上是有所不同的,但元素顺序是相同的。

序列化

二进制数据文件依赖于数据的字节顺序。如果在大端平台上生成了一个二进制数据文件,那么在小端平台上读取文件时,需要转换数据的字节顺序。可以使用 __load2r__load4r__load8r__store2r__store4r__store8r 内置函数来转换字节顺序。例如,当下面的程序在一个大端平台上编译和运行时,就会产生一个二进制文件 bigendian.data

$ cat writefile.c
#include <stdlib.h>
#include <stdio.h>
#include "data.h"
int main() {
  FILE *fp;
  data_t data = { {1,2,3,4,5,6,7,8}, {1,2,3,4}, {-1,-2}, 3.0, 4.0, "abcdefgh" };
  size_t res;
  fp = fopen("bigendian.data", "w");
  if (fp == NULL) {
    perror("fopen");
    exit(1);
  }
  res = fwrite(&data, sizeof(data), 1, fp);
  if (res != 1) {
    perror("fwrite");
    exit(1);
  }
  fclose(fp);
  printf("Wrote:\n");
  print_data(data);
  return 0;
}

$ cat data.h
#ifndef DATA_H
#define DATA_H
#include <stdint.h>
typedef struct {
  int64_t ll[8];
  int32_t i[4];
  int16_t s[2];
  float f;
  double d;
  char c[10];
} data_t;

static void print_data(data_t data) {
  long i;
  printf("data: ll={ ");
 
  for (i = 0; i < 8; ++i) {
    printf("0x%016lx ", data.ll[i]);
  }
  printf("}\n");
  printf("       i={ ");
  for (i = 0; i < 4; ++i) {
    printf("0x%08x ", data.i[i]);
  }
  printf("}\n");
  printf("       s={ ");
  for (i = 0; i < 2; ++i) {
    printf("0x%04hx ", data.s[i]);
  }
  printf("}\n");
  printf("       f=%f\n", data.f);
  printf("       d=%f\n", data.d);
  printf("       c=");
  i = 0;
  while(i < 10 && data.c[i] != '\0') {
    printf("%c", data.c[i]);
    ++i;
  }
  printf("\n");
}
#endif

fwrite 将数据写入为字节流。因此,llisfd 将会采用大端字节顺序。为了在小端平台上读取文件,读取例程不能只使用 fread。它还需要转换数据。例如:

$ cat readfile.c
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "data.h"
static void print_data(data_t data);
int main() {
  FILE *fp;
  data_t data;
  size_t res;
  fp = fopen("bigendian.data", "r");
  if (fp == NULL) {
    perror("fopen");
    exit(1);
  }
  /* This fread call will read all non-character data
     in the wrong byte order */
  res = fread(&data, sizeof(data), 1, fp);
  if (res != 1) {
    perror("fread");
    exit(1);
  }
  printf("Read:\n");
  print_data(data);
  /* Convert the byte order */
  {
    union {
      uint64_t u64;
      uint32_t u32;
      double d;
      float f;
    } tmp;
    long i;
    /* convert the long long data */
    for (i = 0; i < 8; ++i) {
      data.ll[i] = __load8r((uint64_t *) &data.ll[i]);
    }
    /* convert the integer data */
    for (i = 0; i < 4; ++i) {
      data.i[i] = __load4r((uint32_t *) &data.i[i]);
    }      
    /* convert the short data */
    for (i = 0; i < 2; ++i) {
      data.s[i] = __load2r((uint16_t *) &data.s[i]);
    }
    /* convert the float data */
      tmp.f = data.f;
      __store4r(tmp.u32, &data.f);
    /* convert the double data */
    tmp.d = data.d;
    __store8r(tmp.u64, &data.d);
  }
  printf("After conversion:\n");
  print_data(data);
  return 0;
}

当在小端平台上编译和运行 readfile.c 时,生成以下输出。

Read:
data: ll={ 0x0100000000000000 0x0200000000000000 0x0300000000000000 0x0400000000000000 
0x0500000000000000 0x0600000000000000 0x0700000000000000 0x0800000000000000 }
       i={ 0x01000000 0x02000000 0x03000000 0x04000000 }
       s={ 0xffff 0xfeff }
       f=0.000000
       d=0.000000
       c=abcdefgh
After conversion:
data: ll={ 0x0000000000000001 0x0000000000000002 0x0000000000000003 0x0000000000000004 
0x0000000000000005 0x0000000000000006 0x0000000000000007 0x0000000000000008 }
       i={ 0x00000001 0x00000002 0x00000003 0x00000004 }
       s={ 0xffff 0xfffe }
       f=3.000000
       d=4.000000
       c=abcdefgh

当移植依赖于数据的字节顺序的算法时,也需要有类似的考虑。例如,一个函数读取一个包含任意长度的十六进制字符串的文本文件,并将其转换成一个数组 uint64_t,该函数可以将字符串转型为 uint64_t *。虽然这违反了 ANSI 别名规则,但它适用于大端平台,因为 char[8] 数组和 uint64_t 的字节顺序是相匹配的。在小端平台上则不然。

以反向字节顺序进行加载和存储

XL C/C++ 提供以下内置函数来帮助转换字节顺序:

unsigned short __load2r(unsigned short* address)
用反向字节顺序从 address 加载一个无符号的 short。

unsigned int __load4r(unsigned int* address)
用反向字节顺序从 address 加载一个无符号的整数。

unsigned long long __load8r(unsigned long long* address)
用反向字节顺序从 address 加载一个无符号的 long long。

void __store2r(unsigned short source, unsigned short* address)
用反向字节顺序将一个无符号的 short source 存储到 address

void __store4r(unsigned int source, unsigned int* address)
用反向字节顺序将一个无符号的整数 source 存储到 address

void __store8r(unsigned long long source, unsigned long long* address)
用反向字节顺序将一个无符号的 long long source 存储到 address

vec_revb(address)
返回一个与 address 类型相同的矢量,包含采用反向字节顺序的相应 address 元素的字节。

原型:

vector signed char vec_revb(vector signed char);
vector unsigned char vec_revb(vector unsigned char);
vector signed short vec_revb(vector signed short);
vector unsigned short vec_revb(vector unsigned short);
vector signed int vec_revb(vector signed int);
vector unsigned int vec_revb(vector unsigned int);
vector signed long long vec_revb(vector signed long long);
vector unsigned long long vec_revb(vector unsigned long long);
vector float vec_revb(vector float)
vector double vec_revb(vector double);

vec_reve(address)
返回一个与 address 类型相同的矢量,包含采用反向元素顺序的 address 元素。

原型:

vector signed char vec_reve(vector signed char);
vector unsigned char vec_reve(vector unsigned char);
vector signed short vec_reve(vector signed short);
vector unsigned short vec_reve(vector unsigned short);
vector signed int vec_reve(vector signed int);
vector unsigned int vec_reve(vector unsigned int);
vector signed long long vec_reve(vector signed long long);
vector unsigned long long vec_reve(vector unsigned long long);
vector float vec_reve(vector float);
vector double vec_reve(vector double);

参考资料


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=995850
ArticleTitle=定位您的应用程序 - 小端和大端 IBM XL C / C++ 编译器的差异对您意味着什么
publish-date=01222015