以前一直对大小端觉得很绕,什么网络传输采用的字节序是大端序,比特序采用小端序;小端序的机器字节序和比特序都是小端的,大端序的机器都是大端的,把人能够绕晕了,还是不是很理解对吧?
有篇博客写的还挺详细的,参考的是linuxjournal上面的一片文章,一时间找不到了,下次补上来。
被绕晕的原因其实是因为没有实作,最有效的方法就是直接搞一台大端序的虚拟机,vmware好像是不行的(欢迎高手拍砖),这里用到的工具是qemu这个虚拟工具,具体怎么架设呢?其实很简单,感谢下面几篇博客的作者。
https://blog.csdn.net/victor1960/article/details/41577903 这篇博客让我知道哪里可以直接下载到工具。
http://weimingtom.iteye.com/blog/903245 这篇博客让我知道怎么配网络和理解怎么开启虚拟机
https://www.howtoforge.com/using-old-debian-versions-in-your-sources.list 这篇博客让我知道怎么搞定开发环境
(我的主机环境是win10系统)首先我们需要下一些东西:
download qcow2: https://people.debian.org/~aurel32/qemu/
qemu exe: http://qemu.weilnetz.de/
qemu使用的是qemu-w64-setup-20171217.exe这个版本,rc版本我这边跑有bug,没有深究。
qcow2使用的是debian_squeeze_powerpc_standard.qcow2,desktop版本也可以起来,但是莫名的我就是用不了,算了,咱也不折腾了,就用standard的就好了。
东西准备好了,就要配置环境了,主要是要把虚拟机启动起来,并且部署好网络。这里还要下载一个东西openvpn,iteye里面提到的网址应该需要外网才能打开,这里直接提供下载地址,可以用迅雷下载到。
https://swupdate.openvpn.org/community/releases/openvpn-install-2.4.5-I601.exe
安装好之后,修改名字,因为需要访问网络,所以把能上网的网卡开启共享并共享给这个虚拟的网卡,默认会windows会帮你把虚拟网卡的ip设成192.168.137.1, 这样就可以了。
这时候使用qemu来开启虚拟机,命令如下,解释参考iteye上面的说明:
.\qemu-system-ppc.exe -L . -m 1024 -hda .\debian_squeeze_powerpc_standard.qcow2 -localtime -net nic -net tap,ifname=my-tap
这里使用的powerpc的芯片,这个是大端序的。
等待一段时间,有时候感觉卡在boot那里没反应,别管,一般需要个几分钟才能开起来。
登录使用root,密码root
输入ifconfig命令,应该可以发现虚拟机里面的ip应该配好了,也可以照着iteye里面配一下,但是不要去配192.168.200.*的网段了,要用192.168.137.*网段。也许你会发现可以ping通baidu,但是ping不通主机,鄙视自己一下吧,请检查下主机的防火墙是否关了(^_^!)。
下载的镜像应该是没有配置开发环境的,但是呢,你会发现这个系统太老了,apt-get update,install已经不能用了。可能不经常玩debian的人不是很了解怎么弄,方法如下,把/etc/apt/source.list的修改为下面的内容,主要是把ftp改为archive,加上non-free contrib:
deb http://archive.debian.org/debian/ <version> main non-free contrib
deb-src http://archive.debian.org/debian/ <version> main non-free contrib
deb http://archive.debian.org/debian-security/ <version>/updates main non-free contrib
deb-src http://archive.debian.org/debian-security/ <version>/updates main non-free contrib
然后就可apt-get update和install build-essential了。
开发环境建好了,就能够写程序来验证大小端了。
上面是配置大端虚拟机的步骤,下面是重点了,如果你懒得配,那就记住。
如上图,左边的窗口是大端序(powerpc),右边的窗口是小端序(intel,windows)
代码如下
int _tmain(int argc, _TCHAR* argv[])
{
float i = 123.56f;
printf("i:%p %f\n", &i, i);
unsigned char* pi = (unsigned char*)&i;
for (int j = 0; j<sizeof(i);j++)
{
printf("%p %02X\n", &pi[j], pi[j]);
}
union{
int i;
char c;
} ui;
ui.i = 1;
printf("ui.c: %d\n", ui.c);
char ca[6] = "hello";
for (int j = 0; j < 5; j++)
{
printf("%p %c\n", &ca[j], ca[j]);
}
return 0;
}
可以看出:
1. 对于字符串,大端和小端都是从低地址开始存起,所以不需要大小端转换
2. 对于占多个字节的变量,无论是整形还是浮点型,int,long,float等,字节顺序是反的,所以需要转换。不知道为什么windows没有提供float等浮点数的转换函数,注意请用内存拷贝方法,千万别直接就long=float,然后用_byteswap_ulong就给转了,也许新手会犯这个错误,因为c++有个内置类型数据隐式转换的问题,float数据给long是会截取整数部分给long,内存的数据完全改变了,这里新手玩家要注意下。
3. union是给一个内存取了多个名字(姑且这么理解),实际上使用的同一个内存,上面的int占4个字节,char占1个字节,注意这里呢,占用少字节的无论在大端序还是小端序都是从低地址开始的。如果ui.i=1,因为大端序把高字节放在低地址,所以c会是0,小端序呢,低字节在低地址,所以会是1,这样就可以用ui.c是不是1来判断大小端了。
4. 这里有一个看不到的东西,比特序,因为计算机存储的最小单位其实是字节,所以其实很难看到比特序,即使你用位与的方式去看bit0到bit7的数据,但我觉得应该都是cpu已经处理好了的,所以bit0~bit7的数值无论大小端得到的结果是一样的。在网络传输过程中,实际上不需要考虑比特序的问题,因为网卡会根据CPU的大端小端,在发送和接收的时候自动转换为正确的比特序(欢迎高手拍砖)。