解析位元組對齊的資料_位元組對齊不慎引發的掛死問題
技術標籤:解析位元組對齊的資料
作者: 守望,Linux應用開發者,目前在公眾號【程式設計珠璣】分享Linux/C/C++/資料結構與演算法/工具等原創技術文章和學習資源。
前言
之前程式是32位的,切到64位之後,一些隱藏的問題就暴露了。這不,一個由位元組對齊導致的掛死問題就出來了。
位元組對齊和64位
關於位元組對齊,可參考《理一理位元組對齊的那些事》,而之前也分享過另一個切64位之後出現的問題,有興趣的可以檢視《記64位地址截斷引發的掛死問題》。
本文背景
本文出現的場景是,系統需要解析JSON檔案,但是出現部分功能解析正常,部分掛死,並且32位程式正常,而64位程式掛死。鑑於原系統比較複雜,本文將會簡化其過程,來看看到底是什麼導致了掛死。
本文示例程式碼主要引自《一個超輕量級的JSON解析器》。
簡化後示例程式碼
//來源:公眾號【程式設計珠璣】
//https://www.yanbinghu.com
#include
#include
#include
#include
#pragmapack(1)
#include"cJSON.h"
#pragmapack()
編譯執行結果:
$gcc-L.-oparseJsonparseJson.c-lcjson
$./parseJson
Segmentationfault(coredumped)
在實際中我們通過GDB觀察發現,在解析JSON內部檢視JSON資料是完好的,但是呼叫完解析JSON之後,再去訪問使用就不對了,並且我們發現,在不同的功能模組中,呼叫結果不一樣,大部分模組呼叫並沒有任何問題,而只有某個功能模組調用出現問題。
真相
到底是什麼導致的呢?問題的根源在於下面這幾行程式碼:
#pragmapack(1)
#include"cJSON.h"
#pragmapack()
另外補充,cJSON結構體如下:
typedefstructcJSON{//cJSON結構體
structcJSON*next,*prev;/*後驅節點和前驅節點*/
structcJSON*child;/*孩子節點*/
inttype;/*鍵的型別*/
char*valuestring;/*字串值*/
intvalueint;/*整數值*/
doublevaluedouble;/*浮點數值*/
char*string;/*鍵的名字*/
}cJSON;
#pragma指令說明了按一位元組對齊,而cJSON的標頭檔案也在其中,那麼就會導致裡面的cJSON結構體按照1位元組對齊,最終其結構體大小為56個位元組,而已經編譯好的cjson庫可並非如此,因此對於64位程式,它還是按照8位元組對齊,結構體大小為64位元組,而對於32位程式,按照4位元組和1位元組對齊,都是36位元組,因此也不會有問題。
同一個結構體的大小竟然在不同的程式碼中大小不一樣!
最終也就出現了我們遇到的情況,64位程式由於庫中申請結構體記憶體大小與外部呼叫不一樣,最終導致掛死,而32位程式解析JSON正常。
總結
幸運的是,本文示例中能夠很明顯的能看到問題所在,但在實際專案中,如果標頭檔案管理不規範,並且專案的產品多樣,通過編譯巨集來隔開使用的標頭檔案,就很難發現這樣的問題。
思考
什麼情況下需要1位元組對齊呢?
附錄
●編號551,輸入編號直達本文
●輸入m獲取文章目錄
C語言與C++程式設計分享C/C++技術文章