字节序

        字节序是由于不同的主处理器和操作系统,对大于一个字节的变量在内存中的存放顺序不同而产生的,例如2个字节的short int和4个字节的int类型变量都有字节序的问题。

大端字节序和小端字节序

        字节序是由于CPU和OS对多字节变量的内存存储顺序不同而产生的。

字节序介绍

        例如一个16位的整数,它由两个字节构成,它由两个字节构成,在有的系统上会将高字节序放在内存的低地址上,而有的系统上则高字节放在内存的高地址上,所以存在字节序的问题。大于一个字节的变量类型的表示方法有以下方法。

  • 小端字节序(Listen Endian, LE):在表示变量的内存地址的起使地址存放低字节,高字节顺序存放;
  • 大端字节序(Big Endian, BE):在表示变量的内存地址的起使地址存放高字节,低字节顺序存放。

        例如变量的值为0xabcd,在大端字节序和小端字节序的系统中二者的存放顺序是不同的,在小端字节序系统中的存放顺序如下图所示,假设存放值0xabcd的内存地址的起使地址为0,则0xabcd在地址15-8的地址上,而0xcd在地址7-0的位置上。在大端字节序系统中的存放顺序如图所示,假设存放值0xabcd的内存地址起使地址为0,则0xab在地址7-0的地址上,而0xcd在地址15-8的位置上。
在这里插入图片描述

        图中表示的是一个16b的变量的字节序交换方法。在小端字节序主机系统中,进行转换时,将高地址的字节和低地址的字节进行交换;图中表示的是一个32b的变量进行字节序交换的方法,在小端字节序主机系统中,进行字节序交换时,第0个字节的值与第3个字节的值进行交换,第1个字节的值与第2个字节的值进行交换。
        字节交换的作用是生成一个网络字节序的变量,其字节的顺序与主机类型和操作系统无关。进行网络字节序转换的时候,只要转换一次就可以了,不要进行多次的转换。如果进行多次字节序的转换,最后生成的网络字节序的值可能是错误的。例如,对于主机为小端字节序的系统,进行两次字节序转换的过程如下图所示,经过两次转换,最终的值与最初的主机字节序相同。
在这里插入图片描述

字节序的例子

        下面的一段代码用于检查上图所示变量在内存中的表示方法,确定系统中的字节序为大端字节序还是小端字节序。
        (1)字节序结构。程序先建立一个联合类型to,用于测试字节序,成员value是short类型变量,可以通过成员byte来访问value变量的高字节和低字节。

#include <stdio.h>
/*联合类型的变量类型,用于测试字节序;
*成员value的高低端字节可以由成员type按字节访问
*/
typedef union{
	unsigned short int value;	//短整型变量
	unsigned char byte[2];		//字符类型
}

        (2)变量类型。声明一个to类型的变量typeorder,将值0xabcd赋给成员变量value。由于在类型to中,value和byte成员共享一块内存,所以可以通过byte的不同成员来访问value的高字节和低字节。

int main(int argc, char *argv[])
{
	to typeorder;	//一个to类型变量
	typeorder.value = 0xabcd;	//将typeorder变量赋值为0xabcd
}

        (3)小端字节序判断。小端字节序的检查通过判断typeorder变量的byte成员高字节和低字节的值来进行:低字节的值为0xcd,高字节的值为0xab。

/*小端字节序检查*/
	if(typeorder.byte[0] ==0xcd && typeorder.byte[1] ==0xab){
		printf("Low endian byte order"
					"byte[0]:0x%x,byte[1]:0x%x\n",
					typeorder.byte[0],
					typeorder.byte[1]);
	}

        (4)大端字节序判断。大端字节序的检查同样通过判断typeorder变量的byte成员高字节和低字节的值来进行:低字节的值为0xab,高字节的值为0xcd。

//大端字节序检查
	if(typeorder.byte[0] == 0xab && typeorder.byte[1] == 0xcd){
		//高字节在前
		printf("High endian byte order"
				"byte[0]:0x%x,byte[1]:0x%x\n",
				typeorder.byte[0],
				typeorder.byte[1]);
	}
	return 0;
}

        (5)编译运行程序。将上面的代码保存到check_order.c文件中,对文件进行编译后运行,得出如下结果。

$ gcc -o check_order check_order.c
$ ./check_order
Low endian byte orderbyte[0]:0xcd,byte[1]:0xab

        在笔者的系统上,值0xabcd在系统的表达方式为0xab在后,0xcd在前,所以系统是小端字节序。

一个字节序转换的例子

        下面的例子是对16位数值和32位数值进行字节序转换,每种类型的数值进行两次转换,最后打印结果。

16位字节序转换结构

        先定义用于16位字节序转换的结构to16,这个结构是一个联合类型,通过value来赋值,通过byte数组来进行字节序转换。

#include <stdio.h>
#include <arpa/inet.h>
//联合类型的变量类型,用于测试字节序
//成员value的高低端字节可以由成员type按字节访问
//16位字节序转换的结构
typedef union{
	unsigned short int value;	//16位short类型变量value
	unsigned char byte[2];	//char类型数组,共16位

}
32位字节序转换结构

        用于32位字节序转换的结构名称位to32,与to16类似,它也有两个成员变量:value和byte。成员变量value是一个unsigned long int类型的变量,32位长;成员变量byte是一个char类型的数组,数组的长度是4,也是32位长。32位字节序的转换可以通过to32的value成员变量来赋值,通过byte来进行字节序的转换。

//32位字节序转换的结构
typedef union{
	unsigned long int value;	//32位unsigned long类型变量
	unsigned char byte[4];	//char类型数组,共32位
}
变量值打印函数showvalue()

        showvalue()函数用于打印变量值,打印的方式是从变量存储空间的第一个字节开始,按照字节进行打印。showvalue()函数有两个输入参数,一个是变量的地址指针begin,另一个是字长的标志flag。参数flag的值为BITS16的时候打印16位变量的值,参数flag为BITS32的时候打印32位变量的值。

#define BITS16 16	//常量,16
#define BITS32 32	//常量,32
/*按照字节打印,begin为字节开
*flag为BITS16表示16位
*flag位BITS32表示32位
*/
void showvalue(unsigned char *begin, int flag)
{
	int num = 0,i = 0;
	if(flag == BITS16){	//一个16b的变量
		num = 2;
	}else if(flag == BIST32){
		num = 4;
	}
	for(i=0; i<num; i++)	//显示每个字节的值
	{
		printf("%x ",*(begin+i));
	}
	printf("\n");
}
主函数main()

        主函数main()中,先定义用于16位字节序变量转换的变量v16_orig、v16_turn1、v16_turn2,其中v16_ori是16位变量的原始值,v16_turn1是16位变量进行第一次字节序转换后的结果,v16_turn2是16位变量进行第二次字节序转换后的结果(即对变量v16_turn1进行一次字节序转换)。同时定义了用于32位字节序变量转换的变量v32_orig、v32_turn1、v32_turn2,其中v32_orig是32位变量的原始值,v32_turn1是32位变量进行字节序转换后的结果,v32_turn2是32位变量进行第二次字节序转换后的结果(即对变量v32_turn1进行一次字节序转换)。

int main(int argc, char *argv[])
{
	to16 v16_orig, v16_turn1, v16_turn2;	//一个to16类型变量
	to32 v32_orig, v32_tur1, v32_turn2;	//一个to32类型变量
}
16位0xabcd的二次转换

        给16位变量赋初始值0xabcd,然后进行第一次字节序转换,并将结果赋给v16_turn1;进行第二次字节序转换的方式是对v16_turn1进行一次字节序转换。

	v16_orig.value = 0xabcd;	//赋值为0xabcd
	v16_turn1.value = htons(v16_orig.value);	//第一次转换
	v16_turn2.value = htons(v16_turn1.value);	//第二次转换
32位0x12345678的二次转换

        给32位变量赋初始值0x12345678,然后进行第一次字节序转换,并将结果赋给v32_turn1;进行第二次字节序转换的方式是对v32_turn1进行一次字节序转换。

	v32_orig.value = 0x12345678;	//赋值为0x12345678
	v32_turn1.value = htons(v32_orig.value);	//第一次转换
	v32_turn2.value = htons(v32_turn1.value);	//第二次转换
结果打印

        最后将16位变量进行两次字节序转换的结果和32位变量 进行两次字节序转换的结果打印出来

	//打印结果
	printf("16 host to network byte order change:\n");
	printf("\torig:\t");showvalue(v16_orig.byte, BITS16);
	//16位数值的原始值
	printf("\t1 times:");showvalue(v16_turn1.byte, BITS16);
	//16位数值的第一次转换后的值
	printf("\t2 times:");showvalue(v16_turn2.byte, BITS16);
	//16位数值的第二次转换后的值
	printf("32 host to network byte order change:\n");
	printf("\torig:\t");showvalue(v32_orig.byte,BITS32);
	//32位数值的原始值
	printf("\t1 times:");showvalue(v32_turn1.byte, BITS32);
	//32位数值的第一次转换后的值
	printf("\t2 times:");showvalue(v32_turn2.byte, BITS32);
	//32位数值的第二次转换后的值
	return 0;
	}

        16位变量0xabcd在内存中的表示方式为cd在前,ab在后;进行一次字节序转换后变为ab在前,cd在后;进行第二次字节序转换后变为cd在前,ab在后。上面的情况是在笔者的小端字节序系统上的结果,在大端字节序的主机上,即使调用字节序转换函数,字节序也不会发生变化。同时可以发现,在进行第一次转换后字节序发生了变化,而进行第二次字节序转换后与原始的排列方式一致。
        将上述程序中进行第二次转换的主机向网络字节序转换的函数,替换成网络字节序向主机字节序转换的函数,即htons()替换成ntohs(),htonl()替换成ntohl(),结果如下:
在这里插入图片描述
        与不替换的情况完全一致,从结果看好像htons()和ntohs()、htonl()和ntohl()这两个函数没有什么区别。其实在很多平面上htons()和ntohs()、htons()和mtohl()是完全一致的,因为这些函数的本质就是进行字节序的转换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LEO-max

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值