关于内存对齐

一次开发遇到的内存对齐问题

1.问题描述

在开发的过程中有一个协议头,结构如下:

{
    char            cHeadPad         ;       //消息头的标志               1
    char            cMessageFree     ;       //消息预留位                 1   [客户端发来的为0   接管的为1]
    int16_t         nMessageSize     ;       //消息内容指令长度           2
    int16_t         nMessageOrder    ;       //消息指令序号               2
    int16_t         nMessageCmd      ;       //消息指令号                 2
    int16_t         nMessageSCmd     ;       //消息子指令号               2  
    int32_t         nMessageUid      ;       //用户数字ID                 4
    int32_t         nMessageToken    ;       //用户令牌                   4
    char            cMessageCheck    ;       //消息校验位                 1
    char            cMessageEnd      ;       //消息头结束标识位            1
}STR_MSG_HEAD;

协议头的长度被定义为了 20 byte, 所以在接收到数据的时候直接使用如下代码进行填充

#define MSG_HEAD_LEN 20
STR_MSG_HEAD stHead;
memcpy(&(stHead), cMsgHead, MSG_HEAD_LEN );

在使用的过程中发现数据从nMessageUid 开始就是错误的,刚开始没有注意到内存对齐的问题,毕竟旧的代码没有问题,我重构的时候就错了.

问题分析

后来写了一个测试一步步分析,测试代码如下

#include <iostream>
#include <cstring>
#include <cstdio>
#define MSG_HEAD_LEN  20
using namespace std;

typedef struct _STR_MSG_HEAD     //消息结构--头部
{
    char              cHeadPad         ;       //消息头的标志              1
    char              cMessageFree     ;       //消息预留位                1  [客户端发来的为0   接管的为1]
    short int         nMessageSize     ;       //消息内容指令长度          2
    short int         nMessageOrder    ;       //消息指令序号              2
    short int         nMessageCmd      ;       //消息指令号                2
    short int         nMessageSCmd     ;       //消息子指令号              2  
    int               nMessageUid      ;       //用户数字ID                4
    int               nMessageToken    ;       //用户令牌                  4
    char              cMessageCheck    ;       //消息校验位                1
    char              cMessageEnd      ;       //消息头结束标识位          1
}STR_MSG_HEAD;

char peer0_0[] = { /* Packet 29 */
0x08, 
0x00, 
0x4d, 0x00, 
0x00, 0x00, 
0x02, 0x00, 
0x01, 0x00, 
0x49, 0x89, 0xaa, 0x00, 
0x49, 0x89, 0xaa, 0x00, 
0x49, 
0x08};



int main()
{
    STR_MSG_HEAD head;
    memcpy(&head, peer0_0, MSG_HEAD_LEN);
    for (int i = 0; i < MSG_HEAD_LEN ; i++)
    {
        printf("%.2x ", (unsigned char)peer0_0[i]);
    }
    cout << endl;
    cout << (head.cHeadPad&0xff) << endl;
    cout << (head.cMessageFree&0xff) << endl;
    cout << (head.nMessageSize) << endl;
    cout << (head.nMessageOrder) << endl;
    cout << (head.nMessageCmd) << endl;
    cout << (head.nMessageSCmd) << endl;
    cout << (head.nMessageUid) << endl;
    cout << (head.nMessageToken) << endl;
    cout << (head.cMessageCheck&0xff) << endl;
    cout << (head.cMessageEnd&0xff) << endl;

    return 0;
}

输出内容如下

08 00 4d 00 00 00 02 00 01 00 49 89 aa 00 49 89 aa 00 49 08 
8
0
77
0
2
1
-1991704406
139002026
148
255

怀疑大小端问题

在先前的查问题过程中有怀疑到大小端对齐的问题,所以还特别的看了数据 里面head.nMessageToken的值是139002026转换为十六进制是0x84900aa 0xaa是在peer0_0低地址中保存的,也就是说本机中低地址保存了数据的高位
协议头的数据如下

char peer0_0[] = { /* Packet 29 */
0x08, 
0x00, 
0x4d, 0x00, 
0x00, 0x00, 
0x02, 0x00, 
0x01, 0x00, 
0x49, 0x89, 0xaa, 0x00, 
0x49, 0x89, 0xaa, 0x00, 
0x49, 
0x08};

大端存储数据

Big-Endian: 低地址存放高位,如下:
高地址
  ---------------
  peer0_0[19] (0x08) -- 低位
  peer0_0[18] (0x49)
  peer0_0[17] (0x00)
  peer0_0[16] (0xaa) -- 高位
低地址

如果是小端存储那么数据应该是这样的

Little-Endian: 低地址存放低位,如下:
高地址
  ---------------
  peer0_0[19] (0xaa) -- 低位
  peer0_0[18] (0x00)
  peer0_0[17] (0x49)
  peer0_0[16] (0x08) -- 高位
  --------------
低地址

现在来看本机是大端存储的,同时从数据分析来看,head.nMessageToken本应该对应的是0x49, 0x89, 0xaa, 0x00, 现在看来是内存对齐的问题.

内存对齐问题

是旧的代码也是通过memcpy函数直接赋值的。
查询资料了解到,在编译的时候可以控制结构体的内存对齐方式通过#pragma pack() 在代码中对指定的结构体控制内存对齐,

修改代码后测试

#include <iostream>
#include <cstring>
#include <cstdio>
#define MSG_HEAD_LEN  20
using namespace std;


#pragma pack(1)    // 设为1字节对齐
typedef struct _STR_MSG_HEAD     //消息结构--头部
{
    char              cHeadPad         ;       //消息头的标志              1
    char              cMessageFree     ;       //消息预留位                1  [客户端发来的为0   接管的为1]
    short int         nMessageSize     ;       //消息内容指令长度          2
    short int         nMessageOrder    ;       //消息指令序号              2
    short int         nMessageCmd      ;       //消息指令号                2
    short int         nMessageSCmd     ;       //消息子指令号              2  
    int               nMessageUid      ;       //用户数字ID                4
    int               nMessageToken    ;       //用户令牌                  4
    char              cMessageCheck    ;       //消息校验位                1
    char              cMessageEnd      ;       //消息头结束标识位          1
}STR_MSG_HEAD;
#pragma pack()     // 恢复默认对齐方式

char peer0_0[] = { /* Packet 29 */
0x08, 
0x00, 
0x4d, 0x00, 
0x00, 0x00, 
0x02, 0x00, 
0x01, 0x00, 
0x49, 0x89, 0xaa, 0x00, 
0x49, 0x89, 0xaa, 0x00, 
0x49, 
0x08};



int main()
{
    STR_MSG_HEAD head;
    cout << "STR_MSG_HEAD SIZE:" << sizeof(STR_MSG_HEAD) << endl;
    memcpy(&head, peer0_0, MSG_HEAD_LEN);
    for (int i = 0; i < MSG_HEAD_LEN ; i++)
    {
        printf("%.2x ", (unsigned char)peer0_0[i]);
    }
    cout << endl;
    cout << (head.cHeadPad&0xff) << endl;
    cout << (head.cMessageFree&0xff) << endl;
    cout << (head.nMessageSize) << endl;
    cout << (head.nMessageOrder) << endl;
    cout << (head.nMessageCmd) << endl;
    cout << (head.nMessageSCmd) << endl;
    cout << (head.nMessageUid) << endl;
    cout << (head.nMessageToken) << endl;
    cout << (head.cMessageCheck&0xff) << endl;
    cout << (head.cMessageEnd&0xff) << endl;

    return 0;
}

现在问题已经解决,输出如下

STR_MSG_HEAD SIZE:20
08 00 4d 00 00 00 02 00 01 00 49 89 aa 00 49 89 aa 00 49 08 
8
0
77
0
2
1
11176265
11176265
73
8

参考资料

#pragma pack -- 百度
大小端对齐 -- 百度
如何理解大小端对齐 -- 知乎
失传的C结构体打包技艺 -- gitHub

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容