您的位置:首页 > 其它

数据存储方式、传输方式、字节序问题总结

2014-02-26 14:07 429 查看
数据存储方式、传输方式、字节序问题很基础,但也很重要,下面简单总结一下:

1. 数据存储方式(主要以数值和字符类型的数据为例说明)
      1)数据(包括数值和字符)在计算机内存中都是以二进制的形式存放

      数值 --- 补码

      字符 --- 对应的ASCII码

常用数据类型及占用字节
数据类型占用字节数据类型占用字节
bool1unsigned int4
char1long4
short2unsigned long4
short int2float4
unsigned short2double8
int 4void*4
long int4enum4
       
     通过下面的举例,深入理解数值和字符在内存中的存储方式。

       定义变量:

         char szDig[4] = {0x11, 0x00, 0x11, 0x11};

         char szTmp[10] = {0x22, 0x00, 0x22, 0x22};

       【实验一】对szDig分别取strlen和sizeof操作,结果如下:

           strlen(szDig) = 2 (字符串结束符'/0'的ASCII码为0)

           sizeof(szDig) = 4

      【实验二】对szDig和szTmp进行strcat操作

           strcat(szTmp, szDig);

           结果szTmp数组中各项值为 (十六进制):11 22 00 00 00 00 ... ...  

       结论:对于非字符串,即便是char类型数组,也慎用strlen、strcat等字符串操作函数。

                   而如下的定义方式可以通过strlen取长度:

                   char szDig[] = "0000";

      
   2)整型数据在计算机内存中都是以补码的方式存放
      正数的补码是其本身

      负数的补码是其原码(除符号位外)各位取反,然后加1得到

      机器数有三种编码方式:原码、反码和补码。

      原码 --- 有效数值部分照抄,符号位正负(或者+-)分别用0 1表示。

      反码 --- 可由原码得到。

               正数的反码与原码一样;

               负数的反码是其原码(除符号位外)各位取反而得到。

      补码 --- 可由原码得到。

               正数的补码与原码一样;

               负数的补码是其原码(除符号位外)各位取反,并加1而得到。

               

      如,定义整型变量 a 和 b:

      char a, b;

      a = 1;

      b = -5;

      

      则,a(1)在内存中为 00000001

          b(-5)在内存中为 11111011

          即,-5 原码:10000101  反码:11111010  补码:11111011

               
   3)字符型数据在计算机内存中以其对应的ASCII码方式存放
      如,定义字符变量 cha 和 chb:

      char cha, chb;

      cha = 'x';

      chb = '1';

      

      则,cha('x')在内存中为 120,转换成二进制为 1111000

             chb('1')在内存中为 49,转换成二进制为 0110001

2.数据传输
   数据(包括数值和字符)在网络中也都是以二进制 0/1 方式传输,通信双方需提前约定好传输的为字符串还是数值,

   或者哪一部分是字符串,哪一部分是数值,以便接收方能够采用对应的方式读取准确的数据。

  

   借用网上的一个例子来说明数据的发送,如下:

   int i=999;

   char szBuf[1024] = {0};

   char *szTmp = "this is a character";

  

   memcpy(szBuf, &i, sizeof(int));

   memcpy(szBuf + sizeof(int), szTmp, strlen(szTmp));

  

   send(socket, szBuf, sizeof(int) + strlen(szInput), 0);

  

   无论是数值还是字符型的数据,都可以通过内存直接拷贝至待发送的buf。

   (需要注意的就是,不要对非字符串char数组采用strlen获取长度。)

   另外,需要考虑的就是下面要提到的字节序的问题。

  
3. 字节序
     掌握字节序之前,需要了解两个概念:网络字节序和主机字节序

     是否需要考虑字节序转换的两个条件:

     a)对于本地是小端序存储的设备来说,在准备进行网络数据传输时,需要考虑进行字节转换。

     b)如果要发送的数据在本地定义的数据类型(包括一些数据结构的成员)超过2字节,就发送时,就需要考虑字节序转换问题。

        比如,short、long类型的数据

        在发送时,需要分别使用 htons 和 htonl 进行字节序转换;

        在接收时,需要分别使用 ntohs 和 ntohl 进行字节序转换。

   

    下面借用网上一篇帖子来解释一下字主机序和网络序(http://www.cnblogs.com/jacktu/archive/2008/11/24/1339789.html)
   1)主机字节序

      不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序 

      最常见的有两种

      ① Little endian:将低序字节存储在起始地址

      ② Big endian:将高序字节存储在起始地址

      

      LE little-endian (小端序)

      最符合人的思维的字节序 

      地址低位存储值的低位 

      地址高位存储值的高位 

      怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说低位值小,就应该放在内存地址小的地方,

      也即内存地址低位。反之,高位值就应该放在内存地址大的地方,也即内存地址高位。

      

      BE big-endian (大端序)

      最直观的字节序 

      地址低位存储值的高位 

      地址高位存储值的低位 

      为什么说直观,不要考虑对应关系,只需要把内存地址从左到右按照由低到高的顺序写出,

      把值按照通常的高位到低位的顺序写出,两者对照,一个字节一个字节的填充进去。

      

      例子:在内存中双字0x01020304(DWORD)的存储方式 

                 内存地址 4000 4001 4002 4003 

                 LE 04 03 02 01 

                 BE 01 02 03 04 

      

      例子:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为

                              big-endian  little-endian

                  0x0000     0x12        0xcd

                  0x0001     0x34        0xab

                  0x0002     0xab        0x34

                  0x0003     0xcd        0x12

                  x86系列CPU都是little-endian的字节序. 

   
   2)网络字节序

       网络字节顺序,是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,

       从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。

      

       socket编程时,尤其需要注意网络字节序的问题,有下面四个字节序转换函数:

        htons  将unsigned short类型从主机序转换到网络序

        htonl  将unsigned long类型从主机序转换到网络序

        ntohs  将unsigned short类型从网络序转换到主机序

        ntohl  将unsigned long类型从网络序转换到主机序

      

      在使用little endian的系统中 这些函数会把字节序进行转换;

      在使用big endian类型的系统中 这些函数会定义成空宏。

      

      同样,在网络程序开发时,或是跨平台开发时,也应该注意保证只用一种字节序,不然两方的解释不一样就会产生bug。

      

      注:

      ① 网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)

      ② 不同的CPU上运行不同的操作系统,字节序也是不同的,参见下表。

         处理器                操作系统    字节排序

         Alpha                  全部           Little endian

         HP-PA                 NT             Little endian

         HP-PA                UNIX          Big endian

         Intelx86               全部          Little endian <--- x86系统是小端字节序系统

         Motorola680x    全部          Big endian

         MIPS                    NT            Little endian

         MIPS                    UNIX        Big endian

         PowerPC            NT            Little endian

         PowerPC           非NT         Big endian  <--- PPC系统是大端字节序系统

         RS/6000           UNIX          Big endian

         SPARC              UNIX         Big endian

         IXP1200 ARM   全部          Little endian
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息