前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >字节对齐不慎引发的挂死问题

字节对齐不慎引发的挂死问题

作者头像
编程珠玑
发布2019-08-08 17:07:04
1.1K0
发布2019-08-08 17:07:04
举报
文章被收录于专栏:编程珠玑

来源:公众号【编程珠玑】

作者:守望先生

网站:https://www.yanbinghu.com

前言

之前程序是32位的,切到64位之后,一些隐藏的问题就暴露了。这不,一个由字节对齐导致的挂死问题就出来了。

字节对齐和64位

关于字节对齐,可参考《理一理字节对齐的那些事》,而之前也分享过另一个切64位之后出现的问题,有兴趣的可以查看《记64位地址截断引发的挂死问题》。

本文背景

本文出现的场景是,系统需要解析JSON文件,但是出现部分功能解析正常,部分挂死,并且32位程序正常,而64位程序挂死。鉴于原系统比较复杂,本文将会简化其过程,来看看到底是什么导致了挂死。 本文示例代码主要引自《一个超轻量级的JSON解析器》。

简化后示例代码

代码语言:javascript
复制
//来源:公众号【编程珠玑】
//https://www.yanbinghu.com
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<string.h>
#pragma pack(1)
#include"cJSON.h"
#pragma pack()

/*省略部分代码,完整代码可阅读原文查看附录部分*/
int main(void)
{
    char *filename = "./test.json";
    cJSON *pJson = NULL;
    cJSON *pTemp = NULL;
    pJson = prepare_parse_json(filename);
    if(NULL == pJson)
    {
        printf("parse json failed\n");
        return -1;
    }
    /*获取name值*/
    pTemp = cJSON_GetObjectItem(pJson,"name");
    printf("name is %s\n",pTemp->valuestring);
    /*获取site值*/
    pTemp = cJSON_GetObjectItem(pJson,"site");
    printf("site is %s\n",pTemp->valuestring);

    /*获取age值*/
    pTemp = cJSON_GetObjectItem(pJson,"age");
    printf("age is %d\n",pTemp->valueint);
    /*记得释放相关内存*/
    cJSON_Delete(pJson);
    pJson = NULL;
    return ;
}

编译运行结果:

代码语言:javascript
复制
$ gcc -L. -o parseJson parseJson.c -lcjson
$ ./parseJson
Segmentation fault (core dumped)

在实际中我们通过GDB观察发现,在解析JSON内部查看JSON数据是完好的,但是调用完解析JSON之后,再去访问使用就不对了,并且我们发现,在不同的功能模块中,调用结果不一样,大部分模块调用并没有任何问题,而只有某个功能模块调用出现问题。

真相

到底是什么导致的呢? 问题的根源在于下面这几行代码:

代码语言:javascript
复制
#pragma pack(1)
#include"cJSON.h"
#pragma pack()

另外补充,cJSON结构体如下:

代码语言:javascript
复制
typedef struct cJSON {  //cJSON结构体
       struct cJSON*next,*prev;           /*后驱节点和前驱节点*/
       struct cJSON *child;                   /*孩子节点*/
       int type;                                     /* 键的类型*/
       char *valuestring;                       /*字符串值*/
       int valueint;                                /* 整数值*/
       double valuedouble;                    /* 浮点数值*/
       char *string;                               /* 键的名字*/
} cJSON;

#pragma指令说明了按一字节对齐,而cJSON的头文件也在其中,那么就会导致里面的cJSON结构体按照1字节对齐,最终其结构体大小为56个字节,而已经编译好的cjson库可并非如此,因此对于64位程序,它还是按照8字节对齐,结构体大小为64字节,而对于32位程序,按照4字节和1字节对齐,都是36字节,因此也不会有问题。

同一个结构体的大小竟然在不同的代码中大小不一样!

最终也就出现了我们遇到的情况,64位程序由于库中申请结构体内存大小与外部调用不一样,最终导致挂死,而32位程序解析JSON正常。

总结

幸运的是,本文示例中能够很明显的能看到问题所在,但在实际项目中,如果头文件管理不规范,并且项目的产品多样,通过编译宏来隔开使用的头文件,就很难发现这样的问题。

思考

什么情况下需要1字节对齐呢?

附录

本文完整代码可点击阅读原文或者访问 http://www.yanbinghu.com/2019/08/04/21364.html 附录部分。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 字节对齐和64位
  • 本文背景
  • 简化后示例代码
  • 真相
  • 总结
  • 思考
  • 附录
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档