大小端转换定义结构体的技巧
这两年一直在写协议分析和报文填充相关内容。因为PC机是小端(Little Endian),网络序是大端(Big Endian),在写代码的时候必须考虑到大小端转换的问题,否则网卡或者网络设备会解析错误。网上的总结分析甚少,大部分都还处于纠结于大小尾分辨的层次。今天我就来深度分析一下大小尾问题。
首先,不管是哪一种数据存放方式,对于一个单字节的数据,存储方式都是一样的。比如uint8_t类,char类在大端和小端都可以不经过转换直接读取。
而对于两字节及以上字节大小的数据,大小端的存储方式就有区别了。大端(网络序)的存储方式与我们的阅读逻辑是相同的。大端的存储方式是高位保存在低地址中,小端(常见的PC机)的存储方式则是高位存高地址。
举个例子就明白了,比如有个数字0x12345678,存储到内存至少需要4字节,按照大端的存储方式,则是0x12 0x34 0x56 0x78,与我们的阅读逻辑一致;而小端的存储方式则是0x78 0x56 0x34 0x12。记住大端存储方式(网络序)与我们的阅读逻辑相同就可以了。
那么根据上面的分析,我们就会发现,大小端基于内存的颠倒是以字节为单位的,不是以每一位为单位的,这一点在具体的代码实现中尤其重要。
假定我们有一个uint16_t类型的数据,在小端机器上面处理(现在的pc机基本都是小端机)需要转换成大端怎么转换呢?前一字节与后一字节位置换。
假定我们有一个uint32_t类型的数据,在小端机器上面处理(现在的pc机基本都是小端机)需要转换成大端怎么转换呢?前一字节与后一字节置换,中间两个字节也互相置换。
那么,再进一步,如果有一个结构体,大小32位(4字节),定义如下:
struct test_u32{
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
};
在大小端转换时,自然是a和d互换,b和c互换。因此,完全可以在小端定义结构体时,就定义成如下结构:
struct test_u32{
uint8_t d;
uint8_t c;
uint8_t b;
uint8_t a;
};
这样在进行网络通信时就能省去了大小端转换这个步骤,在大小端转换较多的情况下,通过改变结构体定义的方式尽可能省去大小端转换的步骤,对于数据分析和网络传输的速率提升是明显的。
那么接下来再考虑一下情况,如果上述结构体在大端机定义如下:
struct test_u32_2{
uint8_t a;
uint16_t b;
uint8_t c;
};
如果只是在小端机定义成如下形式,会有什么影响吗?
struct test_u32_2{
uint8_t c;
uint16_t b;
uint8_t a;
};
明显的,a和c由于他们的定义不大于1字节,因此a和c的数据不会有错。而由于b大于1字节了,所以仍然需要进行大小端转换。这里就是一个陷阱。
另外在实际的网络私有协议定义中,不可能所有的数据都是基于8位(1字节)对齐,有些数据可能只占1位,有些数据可能占35位,遇到这种情况该如何处理呢?
首先,如果是如下情况,n个变量瓜分一个字节:
struct test_u8{
uint8_t a:1;
uint8_t b:3;
uint8_t c:4;
};
此时变量a占1位,变量b占3位,变量c占4位,他们共享一个字节。一个字节是8位,在内存中的存储方式是“abbbcccc”(每个字符代表该位存储着哪个变量的数据)
对于这种情况,可以直接在小端定义中进行置换:
struct test_u8{
uint8_t c:4;
uint8_t b:3;
uint8_t a:1;
};
在小端如此定义,内存中的数据存储方式依然是abbbcccc。
那么再讨论一个问题,如果在网络序机器中定义如下结构体,小端中也可以如上一例子直接置换吗?
struct test_u16{
uint16_t a:3;
uint16_t b:1;
uint16_t c:12;
};
这个时候我会想当然的认为可以置换成如下形式,但是打印结果告诉我出错了。
struct test_u16{
uint16_t c:12;
uint16_t b:1;
uint16_t a:3;
};
比如我在置换后的结构体中,令a=1,然后按照uint16_t类型输出该结构体数据,按照预想情况应该是0x2000,但是实际打印数据是0x0100。根据打印结果推原因,出错理由如下。
1.在大端中,数据保存方式应当如下:aaabcccc cccccccc。那么如果a=1,b=0,c=0,数据当是0010000 00000000,也就是0x2000。但是现在存储内容成了00000001 00000000,变成了0x0100,那么此时在内存中,数据保存形式为xxxxxaaa xxxxxxxx (x表示未知是b还是c的存储位),经过测试,如此错误定义结构体,使得本来的数据存储方式变为了ccccbaaa cccccccc 。为什么?这个需要自己画图想一下,注意大小端的转换是以字节为单位转换而不是以位为单位转换的。
因此需要修改定义为:
struct test_u16{
uint8_t c_1:4;
uint8_t b:1;
uint8_t a:3;
uint8_t c_2;
};
这时令a=1,b=0,c_1=0,c_2=0,结构体安装uint16_t输出便是0x2000。
结论:在大小端处理时遇到不是整自己的变量,定义结构体以uint8_t为单位定义,对于超过一字节又不足两字节的变量,要拆成两部分处理。
大小端转换定义结构体的技巧相关推荐
- Linux大小端转换实现
实现 #include <byteswap.h> #include <stdint.h>/*** @brief 8字节类型的字节序转化*/ template<class ...
- C语言如何使用其他文件定义的结构体?(C++报错:无法转换到不完整的类【需在头文件中定义结构体??】)
文章目录 20210725 但是,我在使用的时候报错提示:无法转换到不完整的类?? 20210726 这样? 调用时直接加个extern就好,头文件管都不用管? 20210725 但是,我在使用的时候 ...
- c 定义结构体时提示应输入声明_C++|了解结构体的内存对齐(成员声明的顺序影响占用空间大小)...
我们使用的电子计算机绝大部分都是冯·诺依曼结构的机器,遵循"存储程序"的概念.数据处理以存储为前提,在编程中数据如何"存得进去,取得出来",并且符合空间.时间效 ...
- 来点基础的东西,关于浮点数的大小端转换以及浮点数的格式解析
在网上并没有任何信息或则资料介绍关于浮点数的大小端转换的原理的问题,大小端是不同的内存存储实现方式,大端更符合人的阅读习惯,而小端则是更适合CPU读取. 我先说整型,来说明大小端在内存中的存储方式,整 ...
- Go 学习笔记(27)— type 关键字(类型定义、类型别名、类型查询、定义接口、定义结构体)
1. 类型别名定义 定义类型别名的写法为: type TypeAlias = Type 类型别名规定: TypeAlias 只是 Type 的别名,本质上 TypeAlias 与 Type 是同一个类 ...
- c 定义结构体时提示应输入声明_C语言结构体的坑很多,这6大方法千万要记住!...
本文福利在文末! C语言可谓是编程界的传奇语言,历经几十年,依然排名前列. 本文主要说的是C语言中的结构体,结构体是C语言中重要的一部分内容,也是C语言中常用的一种数据结构. 一.关于结构体在C语言中 ...
- 使用函数实现数据大小端转换
使用函数实现数据大小端转换 题目描述 在数字芯片设计中,经常把实现特定功能的模块编写成函数,在需要的时候再在主模块中调用,以提高代码的复用性和提高设计的层次,分别后续的修改. 请用函数实现一个4bit ...
- c 定义结构体时提示应输入声明_C|语法的合理性理解和分析
试想如果你作为C语言或C语言编译器的的设计者,肯定不会任意设置语法规则,除了考虑不能有歧义以外,还会考虑其合理性. 1 效率是第一位的,安全处于次要位置 了解C语言"效率第一.安全次之&qu ...
- Verilog学习之数据大小端转换设计
文章目录 前言 一.题目描述 二.实现思路 1.函数的概念 2.具体思路 三.代码展示 总结 前言 今天我们做的是第十道题--使用函数实现数据大小端转换,这道题其实也比较简单,就是逆向输出一个数据,但 ...
- 【C 语言】结构体 ( 结构体类型定义 | 结构体类型别名 | 声明结构体变量的三种方法 | 栈内存中声明结构体变量 | 定义隐式结构体时声明变量 | 定义普通结构体时声明变量 )
文章目录 一.结构体类型定义 二.结构体类型别名 三.结构体类型变量声明 1.使用结构体类型 ( 别名 ) 声明变量 2. 定义隐式结构体时声明变量 3.定义普通结构体时声明变量 二.完整代码示例 一 ...
最新文章
- mac os x 查看网络端口情况
- Java基础篇:Java集合
- bzoj 1968: [Ahoi2005]COMMON 约数研究【枚举】
- MVC视图之间调用方法总结
- iOS 计算文字宽度的一个细节
- [leedcode 215] Kth Largest Element in an Array
- Eclipse之Undefined attribute name 警告解决办法
- 中职计算机多媒体教材,中职汽车维修课中计算机多媒体教学的实施
- 设计缓存系统:缓存穿透,缓存击穿,缓存雪崩解决方案分析
- 元气开工!这里有一份礼包待领取
- C语言中 整数除法 向上取整的数学证明
- flask-SQLAlchemy数据库密码加密
- 恒指赵鑫:04.11今日恒指早盘思路
- outlook2010 html签名,Outlook2010如何设置签名
- lg g5 android,【LGG5SE评测】Android中的SE 模块化LG G5 SE评测(全文)_LG G5 SE_手机评测-中关村在线...
- C语言项目 - 有理数类型
- 一主双从同步错误 error connecting to master ‘slave@192.168.81.158:3306‘ - retry-time: 60 retries: 1
- 洛谷B2065 鸡尾酒疗法(while限制组数)
- 使用html制作个人主页
- 深度优先搜索DFS | Morris遍历:力扣99. 恢复二叉搜索树
热门文章
- soapui 测试webservice
- ansys workbench 静力结构分析 高阶教程
- “盗版微信”通过反编译手段与正版互联互通,团伙被判刑
- Microsoft Office 2021 简体中文正式版下载
- 联想Y700键盘失灵问题
- 【观察】从实践到赋能再到引领,华为释放数据中心无限潜
- Failed to start The nginx HTTP and reverse proxy server
- linux服务器时间不一致,Linux服务器时间不准确
- 【转】SVN ——开放源代码的版本控制系统
- 【非长篇大论】X3D - Web3D标准的发展