标签:style blog http io color ar os 使用 java
通常的技巧是使用一个指针:
int x = 1; if(*(char *)&x == 1) printf("little-endian\n"); else printf("big-endian\n");
或者一个union:
union { int i; char c[sizeof(int)]; } x; x.i = 1; if(x.c[0] == 1) printf("little-endian\n"); else printf("big-endian\n");
(注意在简单的大端和小端外还有其它字节顺序的可能。)
同时参见问答10.16和20.9b。
参考:H&S Sec. 6.1.2 pp. 163-4
Origin : http://www.c-faq.com/misc/endiantest.html
http://www.c-faq.com/index.html
方法二:
大小端的问题,指的是字长大于8bit的处理器,在处理一个字的时候,将其拆分成多个字节的表示方法。对于大端处理器,高位在低地址,低位在高地址。如0x12345678,在内存中这样表示:
地址 00 01 02 03
数据 12 34 56 78
一般,ARM/MIPS/PPC都是大端处理器。
而对于小端处理器,低位在低地址,高位在高地址。0x12345678这样排列:
地址 00 01 02 03
数据 78 56 34 12
x86属于小端。
这两种排列方法没有好坏之分,但是,对于不同体系结构的处理器之间通讯的时候,有可能产生问题。如MIPS机器发送0x12345678,到了x86接收的时候,就成了0x78563412。
因此,人们制定了网络字节序——和大端字节序一致,高字节先发,低字节后发。这样,对于x86和MIPS/ARM/PPC处理器,对数据的处理流程就不一样了。x86需要将收到的数据按字节序颠倒以后处理,而MIPS/ARM/PPC可以直接处理。编写程序的工程师们为了让代码可移植,定义了以下的宏:
htonl /× 主机序转网络序,long int类型 ×/
ntohl /× 网络序转主机序,long int类型 ×/
htons /× 主机序转网络序,short int类型 ×/
ntohs /× 网络序转主机序,short int类型 ×/
对于小端处理器,这个宏返回颠倒字节序后的值,而对于大端处理器什么都不做。
端模式(Endian)的这个词出自Jonathan Swift书写的《格列佛游记》。这本书根据将鸡蛋敲开的方法不同将所有的人分为两类,从圆头开始将鸡蛋敲开的人被归为Big Endian,从尖头开始将鸡蛋敲开的人被归为Littile Endian。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。在计算机业Big Endian和Little Endian也几乎引起一场战争。在计算机业界,Endian表示数据在存储器中的存放顺序。下文举例说明在计算机中大小端模式的区别。
如果将一个32位的整数0x12345678存放到一个整型变量(int)中,这个整型变量采用大端或者小端模式在内存中的存储由下表所示。为简单起见,本书使用OP0表示一个32位数据的最高字节MSB(Most Significant Byte),使用OP3表示一个32位数据最低字节LSB(Least Significant Byte)。
地址偏移 |
大端模式 |
小端模式 |
0x00 |
12(OP0) |
78(OP3) |
0x01 |
34(OP1) |
56(OP2) |
0x02 |
56(OP2) |
34(OP1) |
0x03 |
78(OP3) |
12(OP0) |
如果将一个16位的整数0x1234存放到一个短整型变量(short)中。这个短整型变量在内存中的存储在大小端模式由下表所示。
地址偏移 |
大端模式 |
小端模式 |
0x00 |
12(OP0) |
34(OP1) |
0x01 |
34(OP1) |
12(OP0) |
由上表所知,采用大小模式对数据进行存放的主要区别在于在存放的字节顺序,大端方式将高位存放在低地址,小端方式将高位存放在高地址。采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。到目前为止,采用大端或者小端进行数据存放,其孰优孰劣也没有定论。
有的处理器系统采用了小端方式进行数据存放,如Intel的奔腾。有的处理器系统采用了大端方式进行数据存放,如IBM半导体和Freescale的PowerPC处理器。不仅对于处理器,一些外设的设计中也存在着使用大端或者小端进行数据存放的选择。
因此在一个处理器系统中,有可能存在大端和小端模式同时存在的现象。这一现象为系统的软硬件设计带来了不小的麻烦,这要求系统设计工程师,必须深入理解大端和小端模式的差别。大端与小端模式的差别体现在一个处理器的寄存器,指令集,系统总线等各个层次中。
编写一段程序判断系统中的CPU 是Little endian 还是Big endian 模式?
分析:
作 为一个计算机相关专业的人,我们应该在计算机组成中都学习过什么叫Little endian 和Big endian。Little endian 和Big endian 是CPU 存放数据的两种不同顺序。对于整型、长整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节);而Little endian 则相反,它认为第一个字节是最低位字节(按照从低地址到高地址的顺序存放数据的低位字节到高位字节)。
例如,假设从内存地址0x0000 开始有以下数据:
0x12 0x34 0xab 0xcd
如 果我们去读取一个地址为0x0000 的四个字节变量,若字节序为big-endian,则读出结果为0x1234abcd;若字节序位little-endian,则读出结果为 0xcdab3412。如果我们将0x1234abcd 写入到以0x0000 开始的内存中,则Little endian 和Big endian 模式的存放结果如下:
地址 0x0000 0x0001 0x0002 0x0003
big-endian 0x12 0x34 0xab 0xcd
little-endian 0xcd 0xab 0x34 0x12
一般来说,x86 系列CPU 都是little-endian 的字节序,PowerPC 通常是Big endian,还有的CPU 能通过跳线来设置CPU 工作于Little endian 还是Big endian 模式。
解答:
显然,解答这个问题的方法只能是将一个字节(CHAR/BYTE 类型)的数据和一个整型数据存放于同样的内存
开始地址,通过读取整型数据,分析CHAR/BYTE 数据在整型数据的高位还是低位来判断CPU 工作于Little
endian 还是Big endian 模式。得出如下的答案:
除了上述方法(通过指针类型强制转换并对整型数据首字节赋值,判断该赋值赋给了高位还是低位)外,还有没有更好的办法呢?我们知道,union 的成员本身就被存放在相同的内存空间(共享内存,正是union 发挥作用、做贡献的去处),因此,我们可以将一个CHAR/BYTE 数据和一个整型数据同时作为一个union 的成员,得出如下答案:
实现同样的功能,我们来看看Linux 操作系统中相关的源代码是怎么做的:
static union { char c[4]; unsigned long mylong; } endian_test = {{ ‘l‘, ‘?‘, ‘?‘, ‘b‘ } };
#define ENDIANNESS ((char)endian_test.mylong)
Linux 的内核作者们仅仅用一个union 变量和一个简单的宏定义就实现了一大段代码同样的功能!由以上一段代码我们可以深刻领会到Linux 源代码的精妙之处!(如果ENDIANNESS=’l’表示系统为little endian,
为’b’表示big endian )
MSB:Most Significant Bit ------- 最高有效位
LSB:Least Significant Bit ------- 最低有效位
大端模式(big-edian)
big-endian:MSB存放在最低端的地址上。
举例,双字节数0x1234以big-endian的方式存在起始地址0x00002000中:
| data |<-- address
| 0x12 |<-- 0x00002000
| 0x34 |<-- 0x00002001
在Big-Endian中,对于bit序列中的序号编排方式如下(以双字节数0x8B8A为例):
----+---------------------------------------------------+
bit | 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 |
----+MSB---------------------------------------------LSB+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
----+---------------------------------------------------+
= 0x8B8A
小端模式(little-endian)
little-endian:LSB存放在最低端的地址上。
举例,双字节数0x1234以little-endian的方式存在起始地址0x00002000中:
| data |<-- address
| 0x34 |<-- 0x00002000
| 0x12 |<-- 0x00002001
在Little-Endian中,对于bit序列中的序号编排和Big-Endian刚好相反,其方式如下(以双字节数0x8B8A为例):
----+---------------------------------------------------+
bit | 15 14 13 12 11 10 09 08 | 07 06 05 04 03 02 01 00 |
----+MSB---------------------------------------------LSB+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
----+---------------------------------------------------+
= 0x8B8A
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。
网络字节顺序是“所见即所得”的顺序。而Intel类型的CPU的字节顺序与此相反。
比如上面的 short B=0102H(十六进制,每两位表示一个字节的宽度)。所见到的是“0102”,按一般数学常识,数轴从左到右的方向增加,即内存地址从左到右增加的话,在内存中这个 short B的字节顺序是:
01 02
这就是网络字节顺序。所见到的顺序和在内存中的顺序是一致的!
在C语言中,不同于结构体,共用体(联合体)中的几种不同类型的变量存放在同一段内存单元中。利用这一特点,可以用联合体变量判断ARM或x86环境下,存储系统是是大端还是小端模式。
#include "stdio.h"
int main()
{
union w
{
int a; //4 bytes
char b; //1 byte
} c;
c.a=1;
if (c.b==1)
printf("It is Little_endian!\n");
else
printf("It is Big_endian!\n");
return 1;
}
说明:
1 在c中,联合体(共用体)的数据成员都是从低地址开始存放。
2 若是小端模式,由低地址到高地址c.a存放为0x01 00 00 00,c.b被赋值为0x01;
————————————————————————————
地址 0x00000000 0x00000001 0x00000002 0x00000003
c.a 01 00 00 00
c.b 01 00
————————————————————————————
3 若是大端模式,由低地址到高地址c.a存放为0x00 00 00 01,c.b被赋值为0x0;
————————————————————————————
地址 0x00000000 0x00000001 0x00000002 0x00000003
c.a 00 00 00 01
c.b 00 00
————————————————————————————
4 根据c.b的值的情况就可以判断cpu的模式了,现在XP环境下的intel CPU是小端模式,不信你可测试下!
的确判别一个 系统 是大头序还是小头序的方法是有的。
比如:
-BEGIN-
int x=1; // 0x00000001
if (*(char*)&x) {
}else{
}
-END-
内存数据(0x00000001)-〉大头/小头字符序-〉数值(1)
这里判别的前提条件是知道内存数据及所对应的数值,所以可以判定出大小头字符序。
在不同的计算机体系结构中,对于数据(比特、字节、字等)的存储和传输机制有所不同;目前在各种体系结构的计算机中,主要采用的字节存储机制主要有两种:大端(Big-endian)和小端(Little-endian)。
字节序,又称端序,尾序,英文:Endianness。在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输的传输顺序。Endianness有时候也可以用指位序(bit)。
“endian”一词来源于乔纳森·斯威夫特的小说格列佛游记。小说中,小人国为水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开而争论,争论的双方分别被称为Big-endians和Little-endians。
1980年,Danny Cohen在其著名的论文”On Holy Wars and a Plea for Peace”中,为平息一场关于字节该以什么样的顺序传送的争论,而引用了该词。
首先有两个概念需要解释一下:
1.MSB(全大写)指the Most Significant Byte,指多字节序列中具有最大权重的字节。
2.LSB(全大写)指the Least Significant Byte,指多字节序列中具有最小权重的字节。
PS:小写的msb,lsb还有另外的意思,最高有效位(the Most Significant Bit,msb)和最低有效位(the least significant bit,lsb)。
大端(Big-endian):MSB放在低地址,LSB存放在高地址。(大端方式将高位存放在低地址)
小端(Little-endian):MSB存放在高地址,LSB存放在低地址。(小端方式将高位存放在高地址)
当然另外,还有一种混合序(mixed-endian or middle-endian),此处暂不解释了。
这个说的有点抽象,下面举个例子吧:
以一个数值为1029的整数(4个字节)为例子吧,其分别在大端和小端架构中的存储方式分别为下表所示:
Big-endian VS. Little-endian
—————————————————————————-
Address | Big-endian | Little-endian |
---|---|---|
0 byte | 00000000 | 00000101 |
1 byte | 00000000 | 00000100 |
2 byte | 00000100 | 00000000 |
3 byte | 00000101 | 00000000 |
—————————————————————————-
在主流的体系结构中,小端(Little-endian)的代表是我们最常用x86架构(包括x86_64),还有 6502 (including 65802, 65C816), Z80 (including Z180, eZ80 etc.), MCS-48, 8051, DEC Alpha, Altera Nios, Atmel AVR, SuperH, VAX, 和 PDP-11 等等;
大端(Big-endian)的架构有 Motorola 6800 and 68k, Xilinx Microblaze, IBM POWER, system/360, System/370 等等。
另外有一些架构的endian是可配置的,如下架构有配置endian为大端、小端中任一种的功能, ARM, PowerPC, Alpha, SPARC V9, MIPS, PA-RISC 和 IA-64 等等。
下面,这个简单的C程序,可以查询出当前的架构是大端还是小端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * To check CPU is Little endian or Big endian. * x86 is little-endian, many of other CPUs are big-endian. * ARM is Bi-endian (configurable endian). * Author: Jay Ren * Date: 2012.01.04 */ #include <stdio.h> int main(int argc, char *argv[]) { int num = 1; if(*(char*)&num == 1) printf("Your CUP is Little Endian.\n"); else printf("Your CUP is Big Endian.\n"); } |
参考:
http://en.wikipedia.org/wiki/Endianness
“Linux Kernel Development” book
一、大端模式和小端模式的起源 关于大端小端名词的由来,有一个有趣的故事,来自于Jonathan Swift的《格利佛游记》:Lilliput和Blefuscu这两个强国在过去的36个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端,可以那时的皇帝的祖父由于小时侯吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端,违令者重罚。然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家Blefuscu的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有11000余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。这个其实讽刺当时英国和法国之间持续的冲突。Danny Cohen一位网络协议的开创者,第一次使用这两个术语指代字节顺序,后来就被大家广泛接受。 二、什么是大端和小端 Big-Endian和Little-Endian的定义如下: 1)大端模式: 低地址 -----------------> 高地址 2)小端模式: 低地址 ------------------> 高地址 可见,大端模式和字符串的存储模式类似。 3)下面是两个具体例子:
16bit宽的数0x1234在Little-endian模式(以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:
32bit宽的数0x12345678在Little-endian模式以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:
4)大端小端没有谁优谁劣,各自优势便是对方劣势: 小端模式 :强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样。
三、数组在大端小端情况下的存储: 以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:
四、为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
五、如何判断机器的字节序可以编写一个小的测试程序来判断机器的字节序:
BOOL IsBigEndian() { int a = 0x1234; char b = *(char *)&a; //通过将int强制类型转换成char单字节,通过判断起始存储位置。即等于 取b等于a的低地址部分 if( b == 0x12) { return TRUE; } return FALSE; }<span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span>联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性可以轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写:
BOOL IsBigEndian() { union NUM { int a; char b; }num; num.a = 0x1234; if( num.b == 0x12 ) { return TRUE; } return FALSE; }<span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span> 六、常见的字节序一般操作系统都是小端,而通讯协议是大端的。 4.1 常见CPU的字节序Big Endian : PowerPC、IBM、Sun 4.2 常见文件的字节序Adobe PS – Big Endian 七、如何进行转换对于字数据(16位):
#define BigtoLittle16(A) (( ((uint16)(A) & 0xff00) >> 8) | \ (( (uint16)(A) & 0x00ff) << 8)) 对于双字数据(32位):
#define BigtoLittle32(A) ((( (uint32)(A) & 0xff000000) >> 24) | \ (( (uint32)(A) & 0x00ff0000) >> 8) | \ (( (uint32)(A) & 0x0000ff00) << 8) | \ (( (uint32)(A) & 0x000000ff) << 24))
八、从软件的角度理解端模式从软件的角度上,不同端模式的处理器进行数据传递时必须要考虑端模式的不同。如进行网络数据传递时,必须要考虑端模式的转换。在Socket接口编程中,以下几个函数用于大小端字节序的转换。
#define ntohs(n) //16位数据类型网络字节顺序到主机字节顺序的转换 #define htons(n) //16位数据类型主机字节顺序到网络字节顺序的转换 #define ntohl(n) //32位数据类型网络字节顺序到主机字节顺序的转换 #define htonl(n) //32位数据类型主机字节顺序到网络字节顺序的转换
#define ___swab16(x){ __u16 __x = (x); ((__u16)( (((__u16)(__x) & (__u16)0x00ffU) << 8) | (((__u16)(__x) & (__u16)0xff00U) >> 8) ));} #define ___swab32(x){ __u32 __x = (x); ((__u32)( (((__u32)(__x) & (__u32)0x000000ffUL) << 24) | (((__u32)(__x) & (__u32)0x0000ff00UL) << 8) | (((__u32)(__x) & (__u32)0x00ff0000UL) >> 8) | (((__u32)(__x) & (__u32)0xff000000UL) >> 24) )); } 在对普通文件进行处理也需要考虑端模式问题。在大端模式的处理器下对文件的32,16位读写操作所得到的结果与小端模式的处理器不同。单纯从软件的角度理解上远远不能真正理解大小端模式的区别。事实上,真正的理解大小端模式的区别,必须要从系统的角度,从指令集,寄存器和数据总线上深入理解,大小端模式的区别。
九、从系统的角度理解端模式先补充两个关键词,MSB和LSB:
MSB:MoST Significant Bit ------- 最高有效位 LSB:Least Significant Bit ------- 最低有效位 处理器在硬件上由于端模式问题在设计中有所不同。从系统的角度上看,端模式问题对软件和硬件的设计带来了不同的影响,当一个处理器系统中大小端模式同时存在时,必须要对这些不同端模式的访问进行特殊的处理。
十、实际中的例子 虽然很多时候,字节序的工作已由编译器完成了,但是在一些小的细节上,仍然需要去仔细揣摩考虑,尤其是在以太网通讯、MODBUS通讯、软件移植性方面。这里,举一个MODBUS通讯的例子。在MODBUS中,数据需要组织成数据报文,该报文中的数据都是大端模式,即低地址存高位,高地址存低位。假设有一16位缓冲区m_RegMW[256],因为是在x86平台上,所以内存中的数据为小端模式:m_RegMW[0].low、m_RegMW[0].high、m_RegMW[1].low、m_RegMW[1].high…… |
标签:style blog http io color ar os 使用 java
原文地址:http://www.cnblogs.com/x113/p/4078102.html