struct字段的布局是否与c#中的端序一致?

本文关键字:字段 布局 是否 struct | 更新日期: 2023-09-27 18:19:05

当我第一次学习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,依次。所以在典型的小端机器上,它在内存中的样子是这样的:

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

我的问题是,CLR如何在大端机器上安排结构体?它是否会让小数点的基本布局保持不变,就像这样

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

还是完全颠倒十进制的二进制排列,比如

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

?

附近不要有大端机器,否则我要自己测试。


edit: TL;DR下面的代码在大端机器上打印-10吗?

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

struct字段的布局是否与c#中的端序一致?

你的实际问题不是很清楚。

关于数据结构中字段的布局与端序的关系,没有。Endianness不影响数据结构中字段的布局,只影响字段内字节的顺序。

。对于这个问题的回答:

下面的代码在大端机器上打印-1还是0 ?

白马王子;输出将是-1

但是你似乎也在或相反地询问端序对Decimal类型的内存表示的影响。这是一个有点不同的问题。


关于Decimal在内存中表示的端序,我不知道有任何要求。net提供Decimal类型的一致实现。正如评论者Hans Passant所指出的,有多种方式来看待当前的实现;要么作为你引用的CLR代码,要么作为更详细的声明,例如wtypes.h或OleDb.h(另一个出现DECIMAL类型的地方,其格式与其他地方相同)。但是在现实中,就。net而言,你并没有得到任何关于该类型的内存布局的承诺。

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


但是如果微软出于一些奇怪的原因决定他们想要。net实现偏离本机实现(似乎不太可能,但为了讨论的缘故,让我们假设它),并且即使在大端编码的平台上也总是使用小端编码的字段,那将是他们的权利。

,他们甚至可以重新排列字段如果他们想:他们当前的订单在我看来是一个让步,事实上的x86 little-endianness标准,这样在低位优先的架构的组合低和中期32位值可视为一个64位的值没有交换的话,如果他们决定偏离wtypes.h声明,他们很可能决定把尾数一个96位,低位优先或高位优先值。

再次强调,我并不是说这些行为在任何方面都是可能的。只是它们在理论上是可能的,并且只是简单的,明显的例子(所有可能的例子的一个子集),为什么编写托管代码,假设这样的私有实现细节可能不是一个好主意。

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

(*)(我甚至不知道现在有任何& help;纯大端cpu是相当少见的,而且我想不出一个我能想到的被微软作为一个真正的。net平台支持的。)


So…

我怀疑它是实用的作者BinaryReaderBinaryWriter的实现明显比那些在。net中已经找到的优化。使用这些类型的主要原因是为了处理I/O,这必然意味着要与外部系统进行交互,这些系统的速度要比处理字节表示之间实际转换的CPU慢几个数量级(甚至是支持这些转换的GC操作)。即使现有的微软代码在某种程度上假设是低效的,在实践中我也怀疑它会有多大影响。

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