little endian和big endian是表示计算机字节顺序的两种格式,所谓的字节顺序指的是长度跨越多个字节的数据的存放形式.
假设从地址0x00000000开始的一个字中保存有数据0x1234abcd,那么在两种不同的内存顺序的机器上从字节的角度去看的话分别表示为:
1)little endian:在内存中的存放顺序是0x00000000-0xcd,0x00000001-0xab,0x00000002-0x34,0x00000003-0x12
2)big endian:在内存中的存放顺序是0x00000000-0x12,0x00000001-0x34,0x00000002-0xab,0x00000003-0xcd
需要特别说明的是,以上假设机器是每个内存单元以8位即一个字节为单位的.
简单的说,ittle endian把低字节存放在内存的低位;而big endian将低字节存放在内存的高位.
现在主流的CPU,intel系列的是采用的little endian的格式存放数据,而motorola系列的CPU采用的是big endian.
以下是判断字节存储顺序的可移植的C语言代码:
/********************************************************************
created: 2006-9-5
filename: test.cpp
author: 李创
purpose: 可移植的用于判断存储格式是
little endian还是big ednian的C代码
取自<<C: A Reference Manual>>
*********************************************************************/
#include <stdio.h>
union
{
long Long;
char Char[sizeof(long)];
}u;
int main()
{
u.Long = 1;
if (u.Char[0] == 1)
{
printf("Little Endian!\n");
}
else if (u.Char[sizeof(long) - 1] == 1)
{
printf("Big Endian!\n");
}
else
{
printf("Unknown Addressing!\n");
}
printf("Now, Let's look at every byte in the memory!\n");
for (int i = 0; i < sizeof(long); ++i)
{
printf("[%x] = %x\n", &u.Char, u.Char);
}
return 0;
}
很多人认为掌握这个知识是不必要,其实不然.在网络编程中,TCP/IP统一采用big endian方式传送数据,也就是说,假设现在是在一个字节顺序是little endian的机器上传送数据,要求传送的数据是0XCEFABOBO,那么你就要以0XBOBOFACE的顺序在unsigned int中存放这个数据,只有这样才能保证存放的顺序满足TCP/IP的字节顺序要求.很多时候,需要自己编写应用层的协议,字节顺序的概念在这个时候就显得及其的重要了.
下面给出的是在big endian和little endian中相互转换的代码,C语言强大的位操作的能力在这里显示了出来:
/********************************************************************
created: 2006-9-5
filename: get32put32.cpp
author: 李创
purpose: 在little endian和big ednian之间相互转化数据的演示代码
*********************************************************************/
#include <stdio.h>
const unsigned char SIZE_OF_UNSIGNEDINT = sizeof(unsigned int);
const unsigned char SIZE_OF_UNSIGNEDCHAR = sizeof(unsigned char);
void put_32(unsigned char *cmd, unsigned int data)
{
int i;
for (i = SIZE_OF_UNSIGNEDINT - 1; i >= 0; --i)
{
cmd = data % 256;
// 或者可以:
//cmd = data & 0xFF;
data = data >> 8;
}
}
unsigned int get_32(unsigned char *cmd)
{
unsigned int ret;
int i;
for (ret = 0, i = SIZE_OF_UNSIGNEDINT - 1; i >= 0; --i)
{
ret = ret << 8;
ret |= cmd;
}
return ret;
}
int main(void)
{
unsigned char cmd[SIZE_OF_UNSIGNEDINT];
unsigned int data, ret;
unsigned char *p;
int i;
data = 0x12345678;
printf("data = %x\n", data);
// 以字节为单位打印出数据
p = (unsigned char*)(&data);
for (i = 0; i < SIZE_OF_UNSIGNEDINT; ++i)
{
printf("%x", *p++);
}
printf("\n");
// 以相反的顺序存放到cmd之中
put_32(cmd, data);
for (i = 0; i < SIZE_OF_UNSIGNEDINT; ++i)
{
printf("cmd[%d] = %x\n", i, cmd);
}
// 再以相反的顺序保存数据到ret中
// 保存之后的ret数值应该与data相同
ret = get_32(cmd);
printf("ret = %x\n", ret);
p = (unsigned char*)(&ret);
for (i = 0; i < SIZE_OF_UNSIGNEDINT; ++i)
{
printf("%x", *p++);
}
printf("\n");
return 0;
}
参考资料:<<C: A Reference Manual>>
[ 本帖最后由 converse 于 2006-10-30 17:30 编辑 ]
coldwarm 回复于:2006-09-05 21:40:35
ittle endian:0xcdab3412,即0x00000000-0xcd,0x00000001-0xab,0x00000002-0x34,0x00000003-0x12
little endian把低位存放到高位
上面的说反了吧.little endian应当是
0x00000000-0x12
0x00000001-0x34
....
这样的吧.
little endian把低字节存放到低地址,把高字节放到高地址.
数据0xcdab3412从左到右,由高字节到低字节,由高地址到低地址.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int value = 0x12345678;
union ValueT
{
int value;
char data[4];
} a;
a.value = 0x12345678;
printf("value is 0x%x\n", a.value);
printf("address is %p, 0x%x\n",&a.data[0], a.data[0]);
printf("address is %p, 0x%x\n",&a.data[1], a.data[1]);
printf("address is %p, 0x%x\n",&a.data[2], a.data[2]);
printf("address is %p, 0x%x\n", &a.data[3], a.data[3]);
exit(EXIT_SUCCESS);
}
value is 0x12345678
address is 0xbffffa0c, 0x78
address is 0xbffffa0d, 0x56
address is 0xbffffa0e, 0x34
address is 0xbffffa0f, 0x12
[ 本帖最后由 coldwarm 于 2006-9-5 21:56 编辑 ]
converse 回复于:2006-09-05 22:21:10
引用:原帖由 coldwarm 于 2006-9-5 21:40 发表
ittle endian:0xcdab3412,即0x00000000-0xcd,0x00000001-0xab,0x00000002-0x34,0x00000003-0x12
little endian把低位存放到高位
上面的说反了吧.little endian应当是
0x00000000-0x12
0x00000001-0x34
. ...
感谢指出错误,我的描述有问题,应该是这样:
引用:
假设从地址0x00000000开始的一个字中保存有数据0x1234abcd,那么在两种不同的内存顺序的机器上从字节的角度去看的话分别表示为:
1)little endian:在内存中的存放顺序是0x00000000-0xcd,0x00000001-0xab,0x00000002-0x34,0x00000003-0x12
2)big endian:在内存中的存放顺序是0x00000000-0x12,0x00000001-0x34,0x00000002-0xab,0x00000003-0xcd
gawk 回复于:2006-09-06 09:22:07
sun的xdr对字节的转换提供了一整套的函数
connet 回复于:2006-09-06 09:30:33
socket 中就有现成的,不需要自己写,也不需要什么 xdr
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
gawk 回复于:2006-09-09 10:41:40
hton等函数适用性不是太强l
#include <rpc/xdr.h>
xdr_array(
XDR *xdrs,
char **arrp,
u_int *sizep,
u_int *maxsize,
u_int *elsize,
xdrproc_t elproc );
xdr_bool(
XDR *xdrs,
bool_t bp );
xdr_bytes(
XDR *xdrs,
char **sp,
u_int *sizep,
u_int maxsize );
xdr_char(
XDR *xdrs,
char*cp );
void xdr_destroy(
XDR *xdrs );
xdr_double(
XDR *xdrs,
double *dp );
xdr_enum(
XDR *xdrs,
enum_t *ep );
xdr_float(
XDR *xdrs,
float *fp );
void xdr_free(
xdrproc_t proc,
char *objp );
u_int xdr_getpos(
XDR *xdrs );
xdr_hyper(
XDR *xdrs,
longlong_t *hp );
long *xdr_inline(
XDR *xdrs,
int len );
xdr_int(
XDR *xdrs,
int *ip );
xdr_long(
XDR *xdrs,
long *lp );
xdr_longlong_t(
XDR *xdrs,
longlong_t *hp );
xdr_opaque(
XDR *xdrs,
char *cp,
u_int cnt );
xdr_pmap(
XDR *xdrs,
struct pmap *regs );
xdr_pmaplist(
XDR *xdrs,
struct pmaplist **rp );
xdr_pointer(
XDR *xdrs,
char **objpp,
u_int objsize,
xdrproc_t xdrobj );
xdr_reference(
XDR *xdrs,
char **pp,
u_int size,
xdrproc_t proc );
xdr_setpos(
XDR *xdrs,
u_int pos );
xdr_short(
XDR *xdrs,
short *sp );
xdr_string(
XDR *xdrs,
char **sp,
u_int maxsize );
xdr_u_char(
XDR *xdrs,
unsigned char *ucp );
xdr_u_hyper(
XDR *xdrs,
u_longlong_t *uhp );
xdr_u_int(
XDR *xdrs,
unsigned *up );
xdr_u_long(
XDR *xdrs,
unsigned long *ulp );
xdr_u_longlong_t(
XDR *xdrs,
u_longlong_t *uhp );
xdr_u_short(
XDR *xdrs,
unsigned short *usp );
xdr_union(
XDR *xdrs,
int *dscmp,
char *unp,
struct xdr_discrim *choices,
bool_t (*defaultarm) (void) );
xdr_vector(
XDR *xdrs,
char *arrp,
u_int size,
u_int elsize,
xdrproc_t elproc );
xdr_void(
void );
xdr_wrapstring(
XDR *xdrs,
char **sp );
void xdrmem_create(
XDR *xdrs,
char *addr,
u_int size,
enum xdr_op op );
void xdrrec_create(
XDR *xdrs,
u_int sendsize,
u_int recvsize,
char *handle,
int (*readit) (void),
int (*writeit) (void) );
这个基本上涵盖了各种类型
snowoak 回复于:2006-09-15 11:58:24
代码应该是:
if (u.Char[ 0 ] == 1 )
{
printf( " [color=Red]Big[/color] Endian!\n " );
}
else if (u.Char[ sizeof ( long ) - 1 ] == 1 )
{
printf( " [color=Red]little[/color] Endian!\n " );
}
[ 本帖最后由 snowoak 于 2006-9-15 12:11 编辑 ]
sway2004009 回复于:2006-09-15 12:10:37
引用:原帖由 snowoak 于 2006-9-15 11:58 发表
还是有问题吧,应该是:
little endian:
0x00000000:0x12
……
0x00000003:0xcd
big endian:
0x00000000:0xcd
……
0x00000003:0x12
是你自己的那个有问题,同意lz的说法
snowoak 回复于:2006-09-15 12:17:11
引用:原帖由 sway2004009 于 2006-9-15 12:10 发表
是你自己的那个有问题,同意lz的说法
呵呵,我知道是我的问题了:)
chb79 回复于:2006-10-30 17:26:02
引用:原帖由 converse 于 2006-9-5 21:12 发表
简单的说,little endian把低位存放到高位,而big endian把低位存放到低位.
这个总结错了吧,应该是
little endian把低字节存放在内存的低位;而big endian将低字节存放在内存的高位。
0x1234adcd中,0xcd算是低字节:)
converse 回复于:2006-10-30 17:30:42
引用:原帖由 chb79 于 2006-10-30 17:26 发表
这个总结错了吧,应该是
little endian把低字节存放在内存的低位;而big endian将低字节存放在内存的高位。
0x1234adcd中,0xcd算是低字节:)
已修改:oops::oops:
mik 回复于:2006-10-30 18:31:18
:wink: 低字节和高字节的说法是不严谨的。
0x11223344 对于 little-endian 来说,0x44是低字节,对 big-endian 来说,0x11是低字节
严谨的用词是: MSB 与 LSB,
也就是说: 对于 little-endian 来说 MSB 在高地址,对 big-endian 来说 MSB 在低地址。
|