字节序转换 偏执的太偏执、 2022-08-08 17:33 227阅读 0赞 [字节序转换][Link 1] 即便有些事情你已经很熟悉了,但是有时候去做的时候还是会错误百出。今天有个同事问我写一个判断机器字节序的函数,如是信手写下来: 1. int is\_big\_endian() 2. \{ 3. int test = 0x12345678; 4. char \*p = (char \*)&p; 5. 6. if(\*p = 0x12) 7. return 1; 8. 9. else 10. return 0; 11. \} “看起来不错,”他说,但是摇了摇头,我知道这家伙一定又在刁难我,但是如果你想进步,如果没有人对你锱铢必较,那么真的不是什么好事!上面的程序至少有一个问题,第一如果sizeof(int) != 4 的情况. 另外如果你听说middle字节序,那么这算是另一个不能算作问题的问题。 记得刚刚毕业的时候写下过一些关于字节序的东西:[http://hi.baidu.com/lli%5Ffly/blog/item/b5c154eecbd5fdfab3fb9575.html/cmtid/34b9c31b8d3715f1af5133e7\#34b9c31b8d3715f1af5133e7][http_hi.baidu.com_lli_5Ffly_blog_item_b5c154eecbd5fdfab3fb9575.html_cmtid_34b9c31b8d3715f1af5133e7_34b9c31b8d3715f1af5133e7]。今天看来还是有所参考的价值。 如果你要尝试编写linux环境下的网络程序,那么一定会碰到这个字节序问题,与其每次碰到的时候都去查找资料,不如一劳永逸,所以就做了一个简图,可以作为随手查看的资料,这里所说的均在自己的机器上验证过,但也都是个人的理解,难免有误。 字节序,就是计算机在安排数据类型存储的时候,怎样存储数据的高低位。其实这一般和操作系统没有关系,而是和计算机的硬件架构有关,具体的说,就是CPU的设计有关,英特尔的芯片和AMD的芯片组都是使用的小端字节序(最低字节优先存储),但是有些特别的CPU就不是这样处理的。它们可能使用大端字节序(最高字节优先存储),这些在不同计算机上使用的字节序称为主机字节序。 另外应明确,计算机CPU可以支持的最基本数据存储是一个字节,数据位的操作使用了特殊的寄存器。所以涉及到字节序问题的数据类型,一定是整字节的倍数,不可能是4bits,也不可是一个字节,六进制表示起来就是:一个数据0xAB,不可能使用字节序转换为0xBA。 为了在不同架构的计算机之间通信,那么网络上的字节序就必须统一,网络字节序使用的是大端字节序(最高字节优先存储),所以我们通常要在程序中做字节序转换,但是为了方便程序的移植,基本上涉及到这类操作的地方都要进行字节序转换。字节序的转换,是当程序在系统上动态运行时进行的,它会根据当前CPU的运行模式来决定转换还是不转换,所以使用字节序转换函数时非常必要的,这让我们的程序移植很容易。 下图给出了很详细的处理函数和相关的数据变化: [![20608849_1308533501NdF3.jpg][]][20608849_1308533501NdF3.jpg] 图中给出了各种资料中常见的名词,它们其实都是指一个东东! 关于数据在网络中是怎么存储的,笔者这样理解,在网关,或者其他网络设备中,在进行数据过滤等处理时可能存在不同的字节序,但是到达我们网卡的数据一定是按照网络字节序来存储数据的,当NIC收到一个数据包的时候,它申请一块内存区,然后存入数据,这时候数据都是按照网络字节序来存储的,当我们的软件进程要读取这里的数据的时候,CPU将找到地址,并根据指令来读取一定的字节数,这个时候,如果我们没有进行字节序转换,并且主机字节序与网络字节序不同,那么问题就出现了,对于CPU来说,假设它一贯(也只能,设计的时候就确定了)从高地址读取并作为数据的高位,那么一切数据都是错误的了,所以,转换函数又开辟了一块内存,并将数据的序列完全翻转过来,这样CPU来读这块数据就是正确的了。 如果只是简单的将那篇文章黏贴过来,就没有太多意义了,回到开始的问题,给出最终的答案: 1. /\* a simple test function for system endian 2. \* addr:0x... 0 | 1 | 2 | 3 3. \* little 0x78|0x56|0x34|0x12 4. \* big 0x12|0x34|0x56|0x78 5. \* ? 6. \* little : return 0; 7. \* big : return 1; 8. \* else : return \-1; 9. \*/ 10. int xendian() 11. \{ 12. unsigned int seed = 0x12345678; 13. char \*p = (char \*)&seed; 14. unsigned char uint\_len = sizeof(seed); 15. 16. /\* big endian for higher address is 0x78(high bytes) \*/ 17. if(\*(p \+ uint\_len \- 4) == 0x12 18. && \*(p \+ uint\_len \- 3) == 0x34 19. && \*(p \+ uint\_len \- 2) == 0x56 20. && \*(p \+ uint\_len \- 1) == 0x78) 21. return 1; 22. 23. if(\*p == 0x78 24. && \*(p \+ 1) == 0x56 25. && \*(p \+ 2) == 0x34 26. && \*(p \+ 3) == 0x12) 27. return 0; 28. 29. return \-1; 30. \} 注意:CPU在处理一个变量地址的时候,是将变量的低地址作为其地址的。(不知道有没有特殊的CPU?或者特殊的存储方式:开始从内存的高地址开始?这里不考虑这些情况了。) 即便考虑了上面所说的问题,笔者依然不能保证在64位的机器上正常运行,因为没有经过测试,但是理解了字节序的基本原理,写出判断64位机器上的字节序的正确函数并不是大问题。 [Link 1]: http://blog.chinaunix.net/uid-20608849-id-378279.html [http_hi.baidu.com_lli_5Ffly_blog_item_b5c154eecbd5fdfab3fb9575.html_cmtid_34b9c31b8d3715f1af5133e7_34b9c31b8d3715f1af5133e7]: http://hi.baidu.com/lli%5Ffly/blog/item/b5c154eecbd5fdfab3fb9575.html/cmtid/34b9c31b8d3715f1af5133e7#34b9c31b8d3715f1af5133e7 [20608849_1308533501NdF3.jpg]: http://blog.chinaunix.net/attachment/201106/20/20608849_1308533501NdF3.jpg
还没有评论,来说两句吧...