结构字段布局是否与C#中的字节顺序一致?

时间:2015-10-24 00:34:40

标签: c# .net struct clr endianness

当我第一次学习字节序时,我对它是如何工作感到非常困惑。我最后通过以下比喻向自己解释:

在大端机器上,int[4]将按如下方式排列:

|       int[4]      |
|int1|int2|int3|int4|

在小端机器上,它会像

那样布局
|       int[4]      |
|1tni|2tni|3tni|4tni|

这样,数组的布局在内存中是一致的,而值本身的排列方式也不同。

现在回答真正的问题:我正在.NET库中编写更多优化版本的BinaryReaderBinaryWriter。我遇到的一个问题是Write(decimal)的实施。十进制包含4个int字段:flags, hi, lo,mid, in that order.所以基本上在典型的小端机器上,它在内存中看起来像这样:

|    lamiced    |
|sgalf|ih|ol|dim|

我的问题是,CLR如何在大端机器上安排结构?它会安排它,以便保存小数的基本布局,如此

|    decimal    |
|flags|hi|lo|mid|

或者它会完全颠倒十进制的二进制排列,如

|    decimal    |
|mid|lo|hi|flags|

不要在附近有一台大端机器,否则我会自己测试一下。

编辑:TL; DR在big-endian计算机上执行以下代码打印-10

struct Pair
{
    public int a;
    public int b;
}

unsafe static void Main()
{
    var p = default(Pair);
    p.a = -1;
    Console.WriteLine(*(int*)&p);
}

1 个答案:

答案 0 :(得分:2)

您的实际问题是什么并不完全清楚。

关于数据结构中字段布局与字节序之间的关系,没有。字节顺序不影响数据结构中字段的布局方式,只影响字段 字段中的字节顺序。

即。回答这个问题:

  

以下代码是否在big-endian机器上打印-1或0?

...输出为-1

但是您似乎也在询问有关字节序对Decimal类型的内存中表示的影响。这是一个有点不同的问题。


关于Decimal内存中表示的字节顺序,我不知道.NET提供Decimal类型的一致实现的任何要求。正如评论者Hans Passant指出的那样,有多种方法可以查看当前的实施情况;或者作为您引用的CLR代码,或者作为例如在例如中引用的更详细的声明。 wtypes.h或OleDb.h(出现DECIMAL类型的另一个地方,其格式与其他地方相同)。但实际上,就.NET而言,您并未承诺任何有关该类型的内存布局的内容。

我希望,为了简化实现,代表3个32位尾数组件的字段可能会受到字节序的影响,单独。 (符号和比例表示为单个字节,因此字节顺序不会影响这些字节)。也就是说,虽然各个32位字段的顺序保持不变 - 高,低,中 - 每个字段中的字节将根据当前平台的字节顺序来表示。


但是如果微软出于某种奇怪的原因决定他们希望.NET实现偏离本机实现(似乎不太可能,但让我们为了争论而假设它)并且总是使用很少 - 即使在大端平台上,也可以在他们的权利范围内进行领域的终结。

就此而言,如果他们愿意,他们甚至可以重新排列这些字段:他们当前的顺序在我看来是对事实上x86标准的小端的让步,这样在little-endian架构上的低组合中间的32位值可以被视为单个64位值而无需交换单词,因此如果他们决定偏离wtypes.h声明,他们可能会决定将尾数设为单个96位,小 - endian或big-endian值。

同样,我并不是说这些行为无论如何都不可能。只是它们在理论上是可能的,并且只是简单明了的例子(所有可能示例的一个子集),为什么编写托管代码假定这样的私有实现细节可能不是一个好主意。

即使您可以访问可以运行.NET库(*)的大端机器,因此可以测试实际行为,今天的当前行为并不能为您提供未来行为的任何保证

(*)(我甚至不知道任何......纯粹的大端CPU现在相当罕见,而且我无法想到一个支持我的头顶的单个CPU由Microsoft作为实际的.NET平台。)


所以......

我怀疑编写BinaryReaderBinaryWriter的实现是否切实可行,这些实现明显比.NET中的优化更优化。使用这些类型的主要原因是处理I / O,这必然意味着与外部系统交互,这些外部系统比处理字节表示的实际转换(甚至支持GC操作)的CPU慢几个数量级。那些转换)。即使现有的Microsoft代码在某种程度上在假设上效率低下,但在实践中我怀疑它是否重要。

但是如果您必须自己实现这些,那么在我看来,处理Decimal类型的唯一安全方法是使用Decimal.GetBits()方法和Decimal.Decimal(int[])构造函数。这些使用明确记录的,与endian无关的机制来转换Decimal类型。它们基于int,其内存表示当然会根据字节顺序而有所不同,但您的代码永远不必担心,因为它只需要处理整个int值,而不是它们的字节表示。