龙空技术网

C语言开发技巧---字节大小端

stepwalker 576

前言:

今天看官们对“c语言字节有什么用”都比较注意,小伙伴们都需要了解一些“c语言字节有什么用”的相关文章。那么小编在网上收集了一些有关“c语言字节有什么用””的相关文章,希望姐妹们能喜欢,同学们一起来了解一下吧!

数据的大小端对不同平台数据传输具有重要意义,在具体使用时需要根据需要进行数据大小端转化,不然就会读出错误的数据。

为什么存在大小端呢?

这是因为在计算机系统中,我们是以字节为单位的,每个地址都对应着一个字节,一个字节为 8bit。但是在C语言中,其中 char 类型为1个字节,int型为4字节(通常看系统为多少位)。对于位数大于8位的处理器,例如16位或32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节存放的问题。因此就导致了大端存储和小端存储模式的问题.

1. 概念:

大端模式:把一个数据的低位字节序存放到高地址处,数据高位字节存放在低地址处;

小端模式:把一个数据的低位字节序存放到低地址处,数据高位字节存放在高地址处;

解释:数据的高低位是对于要存储的数据而言的,高低地址是针对内存来说的(内存以字节为单位)。

大小端其实是指针对数据的两种不同的存储方式,以下为两个例子来进一步说明:

---------------------------------------------------------------------------------------------------------

---举例1:

         unsigned int value = 0x12345678,可以用unsigned char buf[4]来表示value:     1)  大端方式:  低地址存放高位,如下:                buf[3] (0x78) -- 低位        buf[2] (0x56)        buf[1] (0x34)        buf[0] (0x12) -- 高位	   2)小端方式: 低地址存放低位,如下:           buf[3] (0x12) -- 高位        buf[2] (0x34)        buf[1] (0x56)        buf[0] (0x78) -- 低位    	

---------------------------------------------------------------------------------------------------------

---举例2:

定义一个数据 int a =1; a会存放在内存中;存储方式分为大端存储和小端存储。

int型数据占用四个字节,int a = 1;即a=0x00 00 00 01;

---便于记忆,可以总结口决为:大反小正;

2. 使用场景:

一般在不同主机端进行数据通信时会涉及到大小端数据格式的处理,如:网络通信,串口通信等;

网络上数据传输上往往采用大端模式进行数据传输;跨硬件平台进行数据传输,数据格式存在差异,存储字节的顺序可能不同;采用通讯协议每次传输数据的字节有一点限制等. 在通信中,大小端是十分重要的,也就是先发低字节还是先发高字节。通信前必须确认好大小端,否则不能正确解析传送过去的数据。

3. 判断大小端的几种方法:

1) 用指针的方式测试大小端:

		short int x = 0x1122;		char  x0, x1;		short int *p = &x;		x0 = *( (char *)p + 0);  //取低字节存储位置 		x1 = *( (char *)p + 1);	 //取高字节存储位置		if(x0 == 0x22)		{			printf ("[%s]:little end mode!\n", __FUNCTION__);		}		else		{			printf ("[%s]:big end mode!\n", __FUNCTION__);		}		

解析说明:

a. 先定义一个short类型的变量(2个字节),能体现出高位字段和低位字段;

b. 再定义两个char类型字节,用于分开保存short类型的两个字节;

c. 定义一个short双字节类型的指针,并指向数据x;

d. 把short类型指针p强制转换成char *类型,按单个字节进行操作;

从0低位存储位置读取内容,分别放入x0和x1中;

e.按大小端定义进行比较;

如果x0(存储低端位置)的值为0x22(数据低端值 ),即为小端;

反之则为大端;

当然上述也可通过定义4个字节的int 类型进行测试,如:

		int x=0x11223344;		char x0, x1, x2, x3; 		int  *p = &x; 		x0 = *( (char *)p + 0);		x1 = *( (char *)p + 1);		x2 = *( (char *)p + 2);		x3 = *( (char *)p + 3);

大家可以自行测试。

2)使用联合结构体测试大小端:

		typedef union		{			int a;			char b;		}test_union_t;		eEnd_t endian_judge_method2 ()		{			test_union_t   u1;			memset (&u1, 0, sizeof(test_union_t));			u1.a = 1;			if (u1.b == 1)			{				printf ("[%s]:little end mode!\n", __FUNCTION__);			}			else 			{				printf ("[%s]:big end mode!\n", __FUNCTION__);			}		}	

解析说明:

原理:利用联合体的成员共享同一块内存,并且同时只能访问一个成员变量,成员变量的地址都是相同的。

a. 先对int型的变量a赋值。假设这是32位的系统,int型占4个字节,对int型的变量赋值1;应该是0x00000001;

b. 这个1可能被放在高地址,也可能被放在低地址,由此可以判断大小端。

c. 这里有个隐含条件,不管是int型变量还是char型变量,起始地址都是一样的,起始地址都是四个字节里最低的那个字节的地址。

d. 当对a赋值后,四个字节里存放的数据情况:

大端则存储的4个字节:

低地址----->高地址

0X0 ,0X0 ,0X0 ,0X1

小端则存储的4个字节:

低地址----->高地址

0X1 ,0X0 ,0X0 ,0X0

e. 最后可通过直接获取char类型的变量b的值可知具体的大小端情况;

3) 完整代码如下:

		#include <stdio.h>		#include <stdlib.h>		#include <string.h>		typedef enum { ENDIAN_LITTLE, ENDIAN_BIG } eEnd_t;		/*方法1 :使用指针方法来判断大小端方式*/		eEnd_t endian_judge_method1()		{				short int x = 0x1122;				char  x0, x1;				short int *p = &x;				x0 = *( (char *)p + 0);				x1 = *( (char *)p + 1);				if(x0 == 0x22)				{						printf ("[%s]:little end mode!\n", __FUNCTION__);						return ENDIAN_LITTLE;  //little end;				}				else				{						printf ("[%s]:big end mode!\n", __FUNCTION__);						return ENDIAN_BIG;      //big end;				}		}		/*方法2 :使用联合方法来判断大小端方式*/		typedef union		{				int a;				char b;		}test_union_t;		eEnd_t endian_judge_method2 ()		{				test_union_t   u1;				memset (&u1, 0, sizeof(test_union_t));				u1.a = 1;				if (u1.b == 1)				{						printf ("[%s]:little end mode!\n", __FUNCTION__);						return ENDIAN_LITTLE;				}				else 				{						printf ("[%s]:big end mode!\n", __FUNCTION__);						return ENDIAN_BIG;				}		}		int main (int argc, char **argv)		{				endian_judge_method1 ();				endian_judge_method2 ();				return 0;		}			

==》运行结果:

		[endian_judge_method1]:little end mode!		[endian_judge_method2]:little end mode!
4. 大小端转换方法:

原理:就是从低到高顺序重新排列,交换位置;

1)简单大小端转换的宏

	/* 32位数据小端模式 */	#define uint32_data(x)    //定义32位数据,这里x为用户自己定义的需要转化的数据	(uint32_t)((((uint32_t)(x) & 0xff000000) >> 24) |\    //这里是ff000000不是ffff0000,按照每两个字节进行的转化				(((uint32_t)(x) & 0xff000000) >> 8) |\    //数据右移8位				(((uint32_t)(x) & 0x0000ffff) << 8) |\    //数据左移8位				(((uint32_t)(x) & 0x000000ff) << 24)\             )  			 	/* 16位数据小端模式 */             	#define uint16_data(x)    //定义16位数据,这里x为用户自己定义的需要转化的数据	(uint16_t)((((uint16_t)(x) & 0x00ff) << 8) |\			  ((((uint16_t)(x) & 0xff00) >> 8) \          )

2)网络socket开发接口中使用到的字节顺序转换函数:

		#include <arpa/inet.h>		/*****************************************		 *** 将 32位主机字节序数据转换成网络字节序数据		 ***(h:host, n:net,l:long)		 *****************************************/		uint32_t htonl(uint32_t hostint32);				/*****************************************		 *** 将16位主机字节序数据转换成网络字节序数据		  *****************************************/		uint16_t htons(uint16_t hostint16);					/*****************************************		***将 32 位网络字节序数据转换成主机字节序数据		 *****************************************/		uint32_t ntohl(uint32_t netint32);				/*****************************************		***将 16 位网络字节序数据转换成主机字节序数据		 *****************************************/		uint16_t ntohs(uint16_t netint16);

补充说明:

a. 网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,

网络字节顺序采用big-endian排序方式。

b. htons是将整型变量从主机字节顺序转变成网络字节顺序,即大端顺序(big-endian)。

c. htonl,其实是host to network, l是返回类型是long. 将主机数转换成无符号长整型的网络字节顺序。

本函数将一个32位数从主机字节顺序转换成网络字节顺序。

标签: #c语言字节有什么用 #long在c语言中占几个字节 #c语言类型字节大小 #c语言中一个字节是多大 #c语言一个字节等于几个串长