在计算机中是以字节为单位,每一个地址对应一个字节,一个字节8bit。在C中,除了8bit的char以外,还有16bit的short,32位的int,64位long,当然具体要由编译器决定,可以通过sizeof来获取不同类型在内存中占用的字节数。在计算机系统中,当物理单位的长度大于1个字节时,就要区分字节顺序。常见的字节顺序有两种:Big Endian(High-byte first) 和 Litter Endian(Low-byte first),当然还有其他字节顺序,但不常见,例如Middle Endian。
在一个n位二进制数字中n-1位,也就是最左边的位。
指最右边的位。
按照上述关于MSB和LSB的意思,在二进制表达方式中,bit从0开始,从右向左,bit 0位最低有效位,而bit 23为最高有效位。而我们一般称左边的0x07为高位字节,0x15为低位字节。
再通俗一点解释就是:8421的,8这端为高位,1这端为低位,相应的字节则分别称为高位字节和低位字节。
在内存中,多字节对象都是被存储为连续的字节序列。例如在C语言中,一个类型为int的变量n,如果其存储的首个字节的地址为0x1000,那么剩余3个字节地址将存储在0x1001~0x1003。总之,不管具体字节顺序是以什么方式排列,内存地址的分配一般是从小到大的增长。 我们常把0x1000称为低地址端,把0x1003称为高地址端。
搞清楚MSB、LSB、高位字节、低位字节之后,再理解大端和小端,就相当容易了,先看看概念:
以二节中的例子int类型整数123456789位例:
从例子中可以看出小端比较符合人的思维,而大端则看上去非常直观。
注:
1.例子中是假设编译器支持int为32位的前提下,如果是16位,那大端的排列则为0xCD 0x15 0x07 0x5B
2.大小端一般是由CPU架构决定,常见的Intel、AMD的CPU使用的是小端字节,而PowerPC使用的是大端字节序,有些ARM处理器还可以选择大端还是小端模式,具体自行查阅。
3.C#中,字节序跟编译平台所在的CPU相关,例如在Intel x86 CPU架构的windows平台中,C#采用的小端序。而Java由于JVM屏蔽不了不同CPU架构导致额字节序差异,所以默认采用大端字节。所以,大小端模式是由CPU决定,而编译器又可能会改变这种模式。
如果是做跨平台开发时,双方需要协商好字节序,然后根据程序运行的环境,确定是否需要字节序转换。
例如约定的通讯字节序位是Big Endian,默认的window采用的Little Endian,那收到数据后就需要做转换操作。
这里简单记录一下C#位操作符,方便以后自己查阅,也方便理解后面的讲解。
1.重复轮子
using System;
namespace Framework.NetPackage.Common
{
///
/// 字节序转换辅助类
///
public static class Endian
{
public static short SwapInt16(this short n)
{
return (short)(((n & 0xff) << 8) | ((n >> 8) & 0xff));
}
public static ushort SwapUInt16(this ushort n)
{
return (ushort)(((n & 0xff) << 8) | ((n >> 8) & 0xff));
}
public static int SwapInt32(this int n)
{
return (int)(((SwapInt16((short)n) & 0xffff) << 0x10) |
(SwapInt16((short)(n >> 0x10)) & 0xffff));
}
public static uint SwapUInt32(this uint n)
{
return (uint)(((SwapUInt16((ushort)n) & 0xffff) << 0x10) |
(SwapUInt16((ushort)(n >> 0x10)) & 0xffff));
}
public static long SwapInt64(this long n)
{
return (long)(((SwapInt32((int)n) & 0xffffffffL) << 0x20) |
(SwapInt32((int)(n >> 0x20)) & 0xffffffffL));
}
public static ulong SwapUInt64(this ulong n)
{
return (ulong)(((SwapUInt32((uint)n) & 0xffffffffL) << 0x20) |
(SwapUInt32((uint)(n >> 0x20)) & 0xffffffffL));
}
}
}
2.BCL库支持的函数
System.Net.IPaddress.HostToNetworkOrder、System.Net.IPAddress.NetworkToHostOrder,这两个函数的内容实现和上面重复轮子原理一样。
在计算机中,负数以及其绝对值的补码形式表示,不明白可以查阅九中贴出的相关资源。关于负数的字节序跟一般整数的字节序处理没有任何区别。
1.对于gb2312、gbk、gb1&8030、bigs,其编码某个汉字产生的字节顺序,由某编码方案本身决定,不受CPU字节序的影响。其实这几种编码的字节序和大端模式的顺序是一致的。
2.UTF-8
UTF-8和gb系列编码一样,其编码某个汉字产生的字节顺序,由其编码方案决定,不受CPU字节序的影响。无论一个汉字有多少个字节,它的字节序与编码顺序保持一致。
3、Unicode
Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。所以他没有要求如何存储编码后的字节,也就受CPU字节序的影响。
Unicode的具体实现包括UTF-16、UTF-32(当然也包括UTF-8,但由于其编码方式和编码后的字节序与其他Unicode编码实现有较大区别,所以单独拿出来讲解的)。
4、总结
1、网络通讯
在实际的网络通讯中,网络协议例如TCP是规定网络字节序(Network Order)是大端。而针对汉字具体使用什么编码,通讯双方要么提前约定好,要么就需要在数据包中标识好汉字具体使用的编码。
如果在网络通讯中,涉及例如UTF16这样区分大小端的编码,除非按网络协议要求采用大端模式是,否则也要事先约定好,或者在数据包中标识好使用的字节序模式。
2、文件
文件的也会存储汉字,当然也要进行编码。如果采用UTF-16这样的有字节序模式区分的编码,编码规则要求可以在文件头部的BOM(Byte Order Mark)来标记。如果没有标记,除非事先知道字节序的模式,否则只能大小端都试一遍。