Endians,语言,硬件和网络 (注:Endians 可能指字节序问题)

3
我们知道字节序与计算机如何存储数据有关。大端字节序计算机架构包括IBM 370,Motorola 68000和Sun Sparc。小端字节序计算机包括英特尔系列(80486,Pentium等)和VAX。
由于JVM的缘故,Java总是大端字节序。协议的缘故,网络传输通常应该使用大端字节序。
1. C、C++和C#是否会受到运行它们的计算机的影响? 2. 协议的缘故,网络传输通常应该使用大端字节序。如果发送前不调用htons和htonl函数,那么如果发送方是英特尔机器上的C++,则传输的数据将为小端字节序。这样说对吗? 3. 如果我们知道所有客户端和服务器都将使用相同体系结构的计算机并使用相同的程序语言,则我们不需要关心字节序(不需要调用ntohl和htonl函数)。这样说对吗?

1
你是在试图为编写非可移植代码辩护吗?因为目前你拥有一套统一的机器。历史已经多次证明,短视的优化尝试从来没有得到过好结果,因为未来是不可预测的。你基本上是试图将工作推给代码的维护者,在未来来临时,他将不得不修复你的破损的非可移植代码。所以,编写代码时要像维护者知道你住在哪里并拥有一把斧子一样。 - Martin York
还有谁说网络层没有某种形式的内部压缩,可以非常好地处理“网络字节顺序”中的数字。你的过早优化会搞砸它。 - Martin York
4个回答

8
  1. 对于C和C++,至少是的,字节序通常取决于计算机(但也可能取决于编译器)。至于C#,我不知道。
  2. 许多网络协议都是大端字节序,是的。如果您不调用htonl,那么在小端字节序的计算机上,您将无法创建有效的数据包。
  3. 因此,您应该始终调用htonl等函数(或者在您使用的任何语言中使用等价函数)。因为即使您今天拥有同构环境,未来几乎肯定会发生变化。

更具体地说,您应该尽可能靠近接口进行转换,并且在一个地方进行转换。如果您在代码库中散布字节序转换调用,那么很难推断出您的代码是否合理。


1
  1. 计算机之间的二进制数据传输取决于字节序。

  2. C、C++和C#对字节序没有任何要求或限制。

  3. 网络应遵循协议。数字在输入和按照协议写出后被转换为内部格式。它们可以是任何格式进行内部处理。

  4. 只有在计算机之间传输二进制数据(无论是存储在文件中还是立即传输)时才需要考虑字节序。

  5. 浮点数也存在类似的问题。

  6. 许多编程语言都不关心字节序。


1

严格来说,Java使用与其运行的硬件相同的字节序,但对于JVM用户来说并不会显示出来,因为在Java中无法访问原始内存。

  1. 没错,C语言使用当前正在运行的处理器使用的布局。
  2. 正确。
  3. 始终将数据转换为网络字节顺序是一个好习惯。迟早你会后悔没有使用htons(和其他函数),只是因为暂时没有关系。成本通常很小,所以除非你有非常充分的理由,否则请这样做!

如果您正在使用类似JNI的东西,我相信您可以访问原始内存。 - Oliver Charlesworth
@Oli - 是的,那很正确!但是JNI是用C/C++编程而不是Java。我认为有一些方法(至少使用过时的API),可以找出正在运行的计算机的字节序,但是真正的Java程序应该不在意如果写得正确的话。 - Anders Zommarin
我想表达的是JNI是一个显式地将JVM的字节序暴露给用户的地方。 - Oliver Charlesworth

0

在非常抽象的层面上,你必须意识到字节序并且具体了解字节序的唯一时刻是在你进行数据序列化时。这有一个非常明确的含义,在C++语言标准中也有所涉及:

在程序的主要部分中,数据以某种类型的变量形式出现,写成T x;。到目前为止,这很可移植;你的程序总是按照你想要的方式运行,而且你不需要知道x在内部是如何表示的。你知道x的内存从&x开始,长度为sizeof(T)字节,但你不知道其他任何信息。如果你确实想找出来,你就必须将&xT*转换为unsigned char*

一般情况下,强制转换指针是被禁止的(这被称为“类型游戏”),但是标准明确允许这种特定的转换。将其转换为char指针是您唯一可以将不透明类型T的数据序列化为实际字节流的方法。正是在此时,您必须了解字节序(或更一般地说,表示),因为您必须知道字节流按照什么顺序组成T的内部表示。

对于整数类型,您可以不使用强制转换指针,但接口仍然位于从字节流到值的转换处:

unsigned char buf[sizeof(unsigned int)];
unsigned int value;

buf[0] = value; buf[1] = value >> 8; buf[2] = value >> 16; /*...*/  // We chose an endianness!
value = buf[0] + (buf[1] << 8) + (buf[2] << 16) + ... ; // ditto

当使用像readwrite这样的操作时,您会发现需要将值转换为字节流,反之亦然,通常与文件、流或套接字相关。
请注意,对于整数值,我们永远不需要知道程序本身的字节序 - 我们只需要知道字节流使用的字节序!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接