【问题标题】:Same output for htonl() and ntohl() on an integer整数上 htonl() 和 ntohl() 的相同输出
【发布时间】:2012-07-10 12:30:32
【问题描述】:

我在 little-endian [LE] 机器 [Linux,Intel 处理器] 上运行了以下程序。我无法解释下面代码 sn-p 中的 3 个输出。由于机器是 LE,a 的值存储为0x78563412。打印时,它显示其实际值。由于它是一台 LE 机器,我希望 ntohl() 是空操作并显示 0x78563412,它正在这样做。但是,我希望 0x12345678 用于包含 htonl() 的第二个打印语句。有人可以帮我理解为什么它们是一样的吗?

int main() 
{
    int a = 0x12345678; 

    printf("Original - 0x%x\n", (a)); 
    printf("Network - 0x%x\n", htonl(a)); 
    printf("Host - 0x%x\n", ntohl(a)); 

    return 0;
}

输出:

Original - 0x12345678
Network - 0x78563412
Host - 0x78563412

【问题讨论】:

    标签: c endianness


    【解决方案1】:

    由于它是一台 LE 机器,我希望 ntohl() 是空操作

    这是错误的。网络字节序为big-endian,主机字节序为little-endian。因此,ntohlhtonl 都返回其输入的字节交换版本。

    记住,htonl的重点是可以在主机上取一个整数,然后写:

    int i = htonl(a);
    

    结果是i 的内存,当使用网络字节顺序解释时,具有与a 相同的值。因此,如果您将 i 的对象表示写入套接字,而另一端的读取器期望网络字节顺序为 4 字节整数,它将读取 a 的值。

    并显示0x78563412

    这是你打算写的吗?如果ntohl 是一个无操作(或者更确切地说,一个标识函数),那么您的第三行必然会打印与第一行相同的内容,因为您将拥有ntohl(a) == a。这就是您的程序打印的大端实现中发生的情况:

    Original - 0x12345678
    Network - 0x12345678
    Host - 0x12345678
    

    【讨论】:

    • 正如下面提到的@Alok,我期待以下行为将始终成立:x == htonl(ntohl(x))。但这并没有发生,您的解释非常有帮助。
    • @Bhaskar:Brian Roach 的观点也很重要:你从来没有计算过htonl(ntohl(a))。您计算了htonl(a)ntohl(a)
    • demo for big-endian machines。与同架构的little-endian版本相比,可以看出不需要字节交换指令
    【解决方案2】:

    在您的程序中,当您编写int a; 知道a 包含一个主机有序整数,程序不知道这一点。您可以轻松地提供一个已包含网络顺序值的 int。当然,如果您对不按主机顺序的值使用任何算术运算符,如果网络顺序与主机顺序不同,从网络的角度来看,结果将是不正确的。

    但这并不是那么牵强,网络有序值通常在低级结构中完全保持这种方式,在发送之前或刚刚接收之后。

    您的程序有什么问题是,当您调用ntohl() 时,您承诺ntohl() 函数您提供的int 是以网络顺序存储在内存中的一些值。这就是合同。如果它不是真的,该函数将不会执行您所期望的,这就是您所看到的。

    正如在大多数系统(大或小但不是愚蠢的端)上的其他解释,这两个函数通常是相同的,要么是字节反转,要么是无操作。

    【讨论】:

      【解决方案3】:

      因为您通过值传递a,因此这些函数中的任何一个都不会更改它。

      您正在打印htonl()ntohl() 正在返回的内容。

      编辑以添加:我错过了你认为一个不会操作的地方。它不是。两者都将在 LE 机器上做完全相同的事情。反转字节顺序。 ntohl() 期望您将网络字节按顺序传递给它 int

      【讨论】:

        【解决方案4】:

        htonlntohl 是完全相同的功能。他们应该满足htonl(ntohl(x)) == x。它们的命名不同仅用于文档(您明确表示您正在从主机转换为网络或其他方式,即使它是相同的)。因此,在 little-endian 机器上,它们都执行字节交换,而在 big-endian 机器上,它们都是空操作。

        【讨论】:

        • 在一个假设的“愚蠢端”C 实现中,字节既不是大端(订购4321)也不是小端(订购1234),但订购例如3214 ,你仍然会有htonl(ntohl(x)),但htonlntohl 不会做同样的事情,它们会在相反方向进行8 位旋转。我希望不存在这样的架构,但由于htonlntohl 是独立的函数,它可以实现套接字API。
        • @SteveJessop 你当然是对的。这可能是我们有两个不同功能的真正原因。 en.wikipedia.org/wiki/Endianness#Middle-endian
        • @SteveJessop 是的,我刚刚想通了,并试图删除我的评论,但你很快! :-)。
        • @SteveJessop:我以为那叫 VAX-endian?
        • @SamB VAX 使用小端序,只有 VAX 浮点数不同。无论如何mixed endian1234 写为2143 而不是3214
        猜你喜欢
        • 2016-08-23
        • 1970-01-01
        • 2014-07-20
        • 1970-01-01
        • 2013-03-29
        • 2019-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多