C++将整型数据转换成大端或小端存储顺序
大端和小端的概念参考之前博客: 大端/小端,高字节/低字节,高地址/低地址,移位运算
昨晚帮导师从指令中恢复图像的时候,导师要我转换成raw格式,也就是记录图像像素的二进制序列,然后反复强调让我注意大端小端。当时我也没在意,用ofstream的write方法一个个地写进去,发现有部分数据存储顺序和其他的不一致。由于时间要紧,我立刻试了下FILE*然后用"wb"模式打开文件来写,刚好要求的也是小端(因为我的win7系统就是小端存储),结果对了当时也就没管为什么之前C++的ofstream只有部分正确了。
查看了官方文档 http://www.cplusplus.com/reference/fstream/ofstream/open/
open的第二个参数是打开模式的选项,其中有一个是ios_base::binary,代表二进制读写,我没有加上这个选项所以才会在写入二进制文件时出现问题。设置这个选项在写入二进制文件的时候就会和系统存储顺序一样了。
ofstream fout;
fout.open("image2.raw", ios_base::binary);
想起我之前也踩过fopen的坑,linux下不区分文本文件和二进制文件,所以"r"和"w"通用,但是windows下区分这两者,于是要用"rb"和"wb"。而C++为了便于跨平台,为了这种区分文本和二进制的系统加上了binary打开方式。
说起来这里我用的ios_base::binary是没问题的,但是官网ofstream::write()函数的示例代码用的是ofstream::binary,看了眼微软的实现,ofstream直接继承的ios_base的binary字段,所以两者是等价的。考虑设计者在设计的时候可能会认为继承自ios_base的类会做些修改吧,这里用ofstream::binary更为妥当。
再回到大端小端的问题,如果是要跨平台地按照小端顺序写入文件是怎样呢?因为可能你在小端机器上写入的,在大端机器上读取的就会是错误的数值。
网络编程中处理这个的手段是用htonl和htons库函数,通过隐藏底层细节的转换函数,把多字节整型统一按照网络字节序(大端)来存储。但是有时候仍然需要使用小端来存储,所以还是要会手写转换函数,这里以小端为例。
比如对4字节整型:0x11223344,转换成小端是:0x44 0x33 0x22 0x11。
0x44 = 0x11223344 & 0xFF
0x33 = (0x11223344 & 0xFF00) >> 8
0x22 = (0x11223344 & 0xFF0000) >> 16
0x11 = (0x11223344 & 0xFF000000) >> 24
另外,可以发现0xFF00即0xFF<<8,因此,可以很自然地用一个循环实现这个逻辑。
对于2字节和8字节的整型也是类似,所以可以用一个简单的函数模板来实现。
#include <stdint.h> template <typename T> // T must be integer type
T to_little_endian(T x)
{
size_t n = sizeof(T) / sizeof(uint8_t); // 2,4,8 T res;
uint8_t* p = (uint8_t*) &res; for (size_t i = ; i < n; i++)
{
int offset = * i;
p[i] = (x & (0xFF << offset)) >> offset;
} return res;
}
看似这个实现没什么问题,来写份测试代码看看
uint16_t i1 = 0x1122;
uint32_t i2 = 0x11223344;
uint64_t i3 = 0x1122334455667788;
printf("%04x => %04x\n", i1, to_little_endian(i1));
printf("%08x => %08x\n", i2, to_little_endian(i2));
printf("%016lx => %016lx\n", i3, to_little_endian(i3));
结果如下,8字节整型转换出错
1122 => 1122
11223344 => 11223344
1122334455667788 => 1100000055667788
调试发现原因是0xFF,这个整型字面值的默认类型是int,对于类型uint64_t(unsigned long long),int向左移位会造成溢出。所以需要显式地把0xFF转换为uint64_t类型。对于这个函数模板而言,0xFF << offset表示的是x将低位全部置为0,并舍弃高位,所以结果是不大于x的,那么这里0xFF只用转换成T即可。
修改后的代码:
template <typename T> // T must be integer type
T to_little_endian(T x)
{
size_t n = sizeof(T) / sizeof(uint8_t); // 2,4,8 T res;
uint8_t* p = (uint8_t*) &res;
T mask = static_cast<T>(0xFF); for (size_t i = ; i < n; i++)
{
int offset = * i;
p[i] = (x & (mask << offset)) >> offset;
} return res;
}
至于大端,只需要把int offset = 8 * i改成int offset = 8 * (n - i - 1)即可。
OK,说实话C++的坑真是不少,类型方面的陷阱真不少。刚才用cout打印uint8_t类型的时候也是,必须强制转换成int,因为uint8_t和char都是1字节,所以operator<<模板会将类型识别成char,并用打印字符的方式来打印。
C++将整型数据转换成大端或小端存储顺序的更多相关文章
- 整型,长整型,无符号整型等 大端和小端(Big endian and Little endian)
一.大端和小端的问题 对于整型.长整型.无符号整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节):而 Little endian ...
- C语言--测试电脑存储模式(大端存储OR小端存储)
相信大家都知道大端存储和小端存储的概念,这在平时,我们一般不用考虑,但是,在某些场合,这些概念就显得很重要,比如,在 Socket 通信时,我们的电脑是小端存储模式,可是传送数据或者消息给对方电脑时, ...
- 大端和小端(Big endian and Little endian)
一.大端和小端的问题 对于整型.长整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节):而 Little endian 则相反,它 ...
- 大端和小端(big endian little endian)
一.大端和小端的问题 对于整型.长整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节):而 Little endian 则相反,它 ...
- 转!大端模式&小端模式
大端模式&小端模式 在C语言中除了8位的char型之外,还有16位的short型,32位的long型(要看具体的编译器),对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器 ...
- 03大端和小端(Big endian and Little endian)
1.大端和小端的问题 对于整型.长整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节),而 Little endian 则相反 ...
- Java中如何判断当前环境是大端字节顺序还是小端字节顺序
Java非字节类型的基本类型,除了布尔型都是由组合在一起的几个字节组成的.这些数据类 型及其大小总结在表 2-1 中. 表:基本数据类型及其大小 数据类型 大小(以字节表示) Byte 1 Char ...
- 大端模式&小端模式、主机序&网络序、入栈地址高低问题
一.大端模式&小端模式 所谓的“大端模式”,是指数据的低位(就是权值较小的后面那几位)保存在内存的高地址中,而数据的高位,保存在内存的低地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处 ...
- 判断CPU是大端还是小端模式
在小端模式中,低位字节放在低地址,高位字节放在高地址:在大端模式中,低位字节放在高地址,高位字节放在低地址.big-endian和little-endian,51单片机是典型的大端模式,Intel电脑 ...
随机推荐
- SQL Server SqlCacheDependency 缓存依赖
SQL server数据缓存依赖有两种实现模式,轮询模式,通知模式. 1 轮询模式实现步骤 此模式需要SQL SERVER 7.0/2000/2005版本以上版本都支持 主要包含以下几步: 1. ...
- 软工作业No.6 甜美女孩第四周
各成员在Alpha阶段认领的任务 成员 Alpha阶段认领的任务 整个项目预期的任务量 曾祎祺 安排每日任务,每晚总结 16% 邓画月 基础2048+自定义 16% 梁佩诗 负责界面 16% 何颖琪 ...
- L1-025 正整数A+B
题的目标很简单,就是求两个正整数A和B的和,其中A和B都在区间[1,1000].稍微有点麻烦的是,输入并不保证是两个正整数. 输入格式: 输入在一行给出A和B,其间以空格分开.问题是A和B不一定是满足 ...
- 一些java的部署执行编译等命令
编译: javac 参数 -d 指定编译后文件的位置 java 执行java文件 java生成jar文件 java执行jar文件 java生成war文件 war包是一种将web程序捆绑到单个文件上的一 ...
- Beta阶段第1周/共2周 Scrum立会报告+燃尽图 05
作业要求与 [https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284] 相同 版本控制:https://git.coding.net/li ...
- Okhttp对http2的支持简单分析
在< Okhttp之RealConnection建立链接简单分析>一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打开t ...
- 分析苹果代充产业链 汇率差+退款造就三线城市千万富翁_中新游戏研究_Joynews中新游戏
分析苹果代充产业链 汇率差+退款造就三线城市千万富翁_中新游戏研究_Joynews中新游戏 CNG:近日有媒体曝出8月22日这一天,有一家淘宝店卖出了351张面值4000南非南特的App Store ...
- magento首页点击任何产品都出现404错误的问题方法
很简单,只要在其他项目上的根目录上复制一个.htaccess文件到项目跟目录下就可以 了,其实就是是设置伪静态:
- liunx用户管理的基本命令
1.passwd 修改用户密码 2.useradd 用户组名 增加用户组 3.su 用户名 切换用户名 4.usermod 用户更改 5.userdel 用户删除
- ITelephony.aidl
在src下先建立包名为com.android.internal.telephony(右键src > new > package,create package-info.java打钩),然后 ...