c语言大小端转化

c语言大小端转化


补:x86机是小端(修改分区表时要注意),单片机一般为大端

   今天碰一个关于字节顺序的问题,虽然看起来很简单,但一直都没怎么完全明白这个东西,索性就找了下资料,把它弄清楚.
   因为现行的计算机都是以八位一个字节为存储单位,那么一个16位的整数,也就是C语言中的short,在内存中可能有两种存储顺序big-endian和litte-endian.考虑一个short整数0x3132(0x32是低位,0x31是高位),把它赋值给一个short变量,那么它在内存中的存储可能有如下两种情况:
大端字节(Big-endian):
----------------->>>>>>>>内存地址增大方向
short变量地址
      0x1000                 0x1001
_____________________________
                         |
      0x31                 0x32

|_______________|________________
高位字节在低位字节的前面,也就是高位在内存地址低的一端.可以这样记住(大端->高位->在前->正常的逻辑顺序)
小端字节(little-endian):
----------------->>>>>>>>内存地址增大方向
short变量地址
      0x1000                 0x1001
_____________________________
                         |
      0x32                 0x31
|________________|________________
低位字节在高位字节的前面,也就是低位在内存地址低的一端.可以这样记住(小端->低位->在前->与正常逻辑顺序相反)
可以做个实验
在windows上下如下程序
#include<stdio.h>
#include<assert.h>
void main( void )
{
      shorttest;
      FILE*fp;
       
      test =0x3132; //(31ASIIC
码的’1’,32ASIIC码的’2’)
      if ((fp =fopen ("c:\\test.txt", "wb")) == NULL)
             assert(0);
      fwrite(&test,sizeof(short), 1, fp);
      fclose(fp);
}
/
在LINUX下程序
#include <stdio.h>

int main()
{
        int a = 0x12345678;
        char *b = (char *)&a;
        printf("a = %x b = %x\n ",a,b[0]);
        return 0;
}
如果输出的结果是a = 0x12345678 b = 0x78 则说明你的机器是小端存放

///
   然后在C盘下打开test.txt文件,可以看见内容是21,而test等于0x3132,可以明显的看出来x86的字节顺序是低位在前.如果我们把这段同样的代码放到(big-endian)的机器上执行,那么打出来的文件就是12.这在本机中使用是没有问题的.但当你把这个文件从一个big-endian机器复制到一个little-endian机器上时就出现问题了.
   如上述例子,我们在big-endian的机器上创建了这个test文件,把其复制到little-endian的机器上再用fread读到一个short里面,我们得到的就不再是0x3132而是0x3231了,这样读到的数据就是错误的,所以在两个字节顺序不一样的机器上传输数据时需要特别小心字节顺序,理解了字节顺序在可以帮助我们写出移植行更高的代码.
正因为有字节顺序的差别,所以在网络传输的时候定义了所有字节顺序相关的数据都使用big-endian,BSD的代码中定义了四个宏来处理:
#definentohs(n)    //网络字节顺序到主机字节顺序n代表net, h代表host, s代表short
#definehtons(n)    //主机字节顺序到网络字节顺序n代表net, h代表host, s代表short
#definentohl(n)     //网络字节顺序到主机字节顺序n代表net, h代表host, l代表 long
#definehtonl(n)     //主机字节顺序到网络字节顺序n代表net, h代表host, l代表 long
举例说明下这其中一个宏的实现:
#define sw16(x) \

   ((short)(\
      (((short)(x)&(short)0x00ffU)<< 8) |\
      (((short)(x)&(short)0xff00U)>> 8)))
这里实现的是一个交换两个字节顺序.其他几个宏类似.

我们改写一下上面的程序
#include<stdio.h>
#include<assert.h>

#define sw16(x) \
   ((short)(\
      (((short)(x)& (short)0x00ffU)<<8) | \
      (((short)(x)& (short)0xff00U)>>8) ))

// 因为x86下面是低位在前,需要交换一下变成网络字节顺序
#define htons(x) sw16(x)
void main( void )
{
      shorttest;
      FILE*fp;
      
      test=htons(0x3132);//(31ASIIC码的’1’,32ASIIC码的’2’)

      if((fp = fopen ("c:\\test.txt", "wb"))==NULL)
             assert(0);
      fwrite(&test,sizeof(short), 1, fp);
      fclose(fp);
}
   如果在高字节在前的机器上,由于与网络字节顺序一致,所以我们什么都不干就可以了,只需要把#definehtons(x)sw16(x)宏替换为 #define htons(x) (x).
   一开始我在理解这个问题时,总在想为什么其他数据不用交换字节顺序?比如说我们write一块buffer到文件,最后终于想明白了,因为都是unsignedchar类型一个字节一个字节的写进去,这个顺序是固定的,不存在字节顺序的问题,够笨啊..

 

http://hi.baidu.com/liyangzhao/blog/item/277e2ce7e105cf2db838200f.html

文章三:

big-endian和little-endian这两个术语来自JonathanSwift在十八世纪的嘲讽作品Gulliver’sTravels。Blefuscu帝国的国民被根据吃鸡蛋的方式划分为两个部分:一部分在吃鸡蛋的时候从鸡蛋的大端(bigend)开始,而另一部分则从鸡蛋的小端(little end)开始。

x86的CPU使用的是LE(Windows中称为“主机字节序”),而SocksAddr中使用的则是BE(就是“网络字节序”),所以在使用网络编程时需要使用htns,htnl,nths,nthl来倒字节序。

其实对汇编熟了就清楚了,惨,我的汇编很惨的

LE little-endian
最符合人的思维的字节序
地址低位存储值的低位
地址高位存储值的高位
怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说
低位值小,就应该放在内存地址小的地方,也即内存地址低位
反之,高位值就应该放在内存地址大的地方,也即内存地址高位

BE big-endian
最直观的字节序
地址低位存储值的高位
地址高位存储值的低位

为什么说直观,不要考虑对应关系
只需要把内存地址从左到右按照由低到高的顺序写出
把值按照通常的高位到低位的顺序写出
两者对照,一个字节一个字节的填充进去


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

内存地址
4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04

MSDN中关于LE和BE的解释
Byte Ordering Byte ordering Meaning
big-endian The most significant byte is on the left end ofaword.
little-endian The most significant byte is on the right end ofaword.
这里这个最重要的字节可以解释成值的最高位,如果换成是钱的话就是最值钱的那一位
比如我有1234元人民币,最值钱的是1000元,最不值钱的是4元,那么这个1就是最重要的字节

Big endian machine: It thinksthefirst byte it reads is the biggest.
Little endian machine: It thinks the first byte it reads isthelittlest.
举个例子,从内存地址0x0000开始有以下数据
0x0000   0x12
0x0001   0x34
0x0002   0xab
0x0003   0xcd
如果我们去读取一个地址为0x0000的四个字节变量,若字节序为big-endian,则读出
结果为0x1234abcd;若字节序位little-endian,则读出结果为0xcdab3412.
如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
         big-endian  little-endian
0x0000   0x12         0xcd
0x0001   0x23         0xab
0x0002   0xab         0x34
0x0003   0xcd         0x12
x86系列CPU都是little-endian的字节序.

在提供的代码中,有一个函数名为`reverse_byte`,它是一个用于进行C语言大小端转化的函数。该函数的定义如下: ``` unsigned int reverse_byte(char *c, char num) { unsigned int r = 0; int i; for (i=0; i<num; i++) { r |= (*(c + i) << (((num-1)*8)-8*i)); } return r; } ``` 该函数的作用是将输入的字节序列进行大小端转换,并返回转换后的结果。具体来说,函数接受两个参数:一个指向字节序列的指针`c`和字节序列的长度`num`。函数通过使用位运算和指针操作来实现大小端转换。在循环中,函数将每个字节与指定的偏移量相乘,并将结果与转换后的结果进行按位或操作。最后,函数返回转换后的结果。 需要注意的是,该代码中还包含了一个`main`函数和一个无限循环,用于接受用户输入并调用`reverse_byte`函数进行大小端转换。但是这部分代码与问题无关,可以忽略不计。 综上所述,提供的代码中的`reverse_byte`函数是一个用于进行C语言大小端转化的函数。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [C语言大小端转换](https://blog.csdn.net/weixin_38825407/article/details/86218667)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [电子学会青少年软件编程(C语言一级)等级考试试卷(2021年6月)-含答案和解题思路.pdf](https://download.csdn.net/download/gozhuyinglong/88230811)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值