专栏首页编程珠玑谈一谈字节序的问题

谈一谈字节序的问题

前言

字节序关系到我们的网络数据能否被正确地解析或使用。那么什么是字节序?又怎么处理字节序的问题呢?本文就来谈一谈字节序的问题。

什么是字节序

字节序指的是多字节的数据各字节的存储顺序。在几乎所有计算机中,多字节数据被存储为连续的字节序列。例如,一个4字节的int类型变量a,其存储的起始地址为0x804900,那么a的四个字节将被分别存在0x804900,0x804901,0x804902,0x804903的位置。但是问题来了,a的最低有效位可以存储在最前面,也可以存储在高最后面,就有两种不同的存储顺序。这就引出了大端序和小端序。

大端序和小端序

实际上,如果最低有效位在最高有效位的前面,则该存储规则为小端序;反之,如果最低有效位在最高有效的后面,则该存储规则为大端序。不同的处理器体系,采用的字节序可能是不同的。例如,x86采用小端序,而PowerPc 970等采用大端序。那么如此一来,不同机器之间的数据传输是不是会出问题呢?

本地序和网络序

本地序(也称主机序)即指前面处理器本身所采用的字节序,因此有的大端序,有的小端序。而网络序,是指网络传输采用的字节序。所幸,网络序是标准化的,即一般统一采用大端序。因此,发送网络数据之前需要将数据转换为网络序,从而避免了前面所担心的问题。而C语言也针对整型数据提供了一组接口,htonl、htons用于本地序转网络序,以及ntohl、ntohs用于网络序转本地序。

示例

我们通过一个例子来观察大端序和小端序,本地序和网络序的不同。示例程序做的事情很简单,定义整型变量a,将a的每一个字节的地址和值打印出来,将其转换为网络序之后,再打印观察。程序清单如下:

/*================================================================
*   Copyright (C) 2018  Ltd. All rights reserved.
*   
*   文件名称:endian.c
*   创 建 者:shouwang
*   创建日期:2018年10月02日
*   描    述:
*
================================================================*/

#include<stdio.h>
#include<arpa/inet.h>
/**将char类型逐个打印成十六进制形式**/
void stringToHex(char *string,unsigned int len)
{
    unsigned int loop = 0;
    char *temp = string;
    if(NULL == temp)
    {
        printf("input para is NULL\n");
        return;
    }
    for(loop = 0; loop < len; loop++)
    {
        printf("%p:0x%2x\n",temp,*(temp));
        temp++;
    }

}
int main(int argc,char *argv[])
{
    /**x86为小端序**/
    printf("转换之前\n");
    int a = 0x12345678;
    printf("a = %d\n",a);
    stringToHex((char*)&a,sizeof(int));
    /*转为网络字节序之后再打印*/
    printf("转换之后\n");
    a = htonl(a);/*转换为网络序*/
    printf("a = %d\n",a);
    stringToHex((char*)&a,sizeof(int));
    return 0 ;
}

编译并运行:

gcc -o endian endian.c 
./endian
转换之前
a = 305419896
0x7ffc2018d844:0x78
0x7ffc2018d845:0x56
0x7ffc2018d846:0x34
0x7ffc2018d847:0x12
转换之后
a = 2018915346
0x7ffc2018d844:0x12
0x7ffc2018d845:0x34
0x7ffc2018d846:0x56
0x7ffc2018d847:0x78

由于本人使用的是x86系列处理器,且编译时未使用交叉编译,因此本地序为小端序。我们可以通过readelf -h endian看到:

readelf -h endian
ELF 头:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          Advanced Micro Devices X86-64
(其他内容未显示...)

观察运行结果,我们可以发现以下几件事情:

  • 小端序a低位的0x78存储在低地址,而高位的12存储在高地址,也就是说对于小端序,其低位存储在高位之前。
  • 使用htonl宏将a转为网络序(大端序)之后,a的低位存储在高位之后。
  • 转换前后,打印a的数值大小截然不同。

为什么不统一字节序

既然每次都发送网络数据之前都要转换,为什么不统一字节序呢?实际上,大小端各有优劣:

  • 计算都是从低位开始的,因此计算机内部处理采用小端序,效率较高。
  • 而大端序存储的时候,由于符号位在高位,因此对于数据征服或大小的判断也就方便许多。另外,大端序也更符合人类的阅读习惯。

再由于各个芯片厂商的坚持,字节序的问题也就一直没有统一。大小端争端起源于吃鸡蛋时先打破大端还是小端,有兴趣的读者可以搜索一下。

总结

在网络应用中,字节序的问题不可忽略,否则可能出现无法预知的问题(如果两台机器本地序相同,且都不做字节序转换,那么侥幸不会出现什么问题)。通过前面的介绍和分析,我们总结出以下几点:

  • 不同处理器之间采用的字节序可能不同。
  • 有些处理器的字节序是确定的,有些处理器的字节序是可配置的。
  • 网络序一般统一为大端序。
  • 数据从本地传输到网络,需要转换为网络序,接收到的网络数据需要转换为本地序后使用。
  • C提供了一组接口用于整型数据在本地序和网络序之间的转换。
  • 多字节数据对象才需要转字节序,例如int,short等,而char不需要。
  • 由于处理器是按照IEEE标准处理float和double的(参考:对浮点数的一些理解),因此也不需要转字节序。
  • 由于Java虚拟机的存在,Java不需要考虑大小端的问题。

本文分享自微信公众号 - 编程珠玑(shouwangxiansheng),作者:守望先生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 理一理字节对齐的那些事

    字节对齐是我们初学C语言就会接触到的一个概念,但是到底什么是字节对齐?对齐准则又是什么?为什么要字节对齐呢?字节对齐对我们编程有什么启示?本文将简单理一理字节对...

    编程珠玑
  • 为什么要初始化?

    什么是初始化?为什么要初始化?静态变量和局部变量的初始化又有什么区别?实际应用中应该怎么做?本文将一一回答这些问题。

    编程珠玑
  • C语言入坑指南-被遗忘的初始化

    什么是初始化?为什么要初始化?静态变量和局部变量的初始化又有什么区别?实际应用中应该怎么做?本文将一一回答这些问题。

    编程珠玑
  • 字节序转换详解

    在跨平台和网络编程中我们经常会提到网络字节序和主机字节序,如果没有正确对两者进行转换,从而导致两方产生了不同的解释,就会出现意想不到的bug。

    无心的梦呓
  • 计算机编码基础

         乱码是我们在日常的工作中经常遇到的问题,你可能从网上好不容易下载了一个炫酷的jQuery插件,但是却在打开的时候,发现某几个js文件都是类似“澶у0?...

    Single
  • 理解字节序

    1. 计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。 举例来说,数值0x2211使用两个字节储存...

    ruanyf
  • 字节和字符

    位(bit):是计算机 内部数据 储存的最小单位,11001100是一个八位二进制数。

    泰斗贤若如
  • UTF8变长编码

    首先要说明的是我们所有的信息都是以二进制字节的形式保存的,比如00000000这样的8个0就代表一个字节了。

    小蜜蜂
  • mysql 数据类型及占用字节数

    秋日芒草
  • 刨根究底字符编码之九——字符编码方案的演变与字节序

    前文已经提及,编号字符集CCS(简称字符集)与字符编码方式CEF(简称编码方式)这两个概念,在早期并没有必要严格区分。

    用户1876609

扫码关注云+社区

领取腾讯云代金券