以字元串列常數當索引鍵值

最近同人在開發資料剖析的程式時,碰到一個很奇怪的現象。我使用 map 容器來存放剖析資料的結果,然而卻發現以原先新增資料所用的相同鍵值,竟然會找不到該筆資料。但我的測試程式顯示容器中所存放的鍵值與資料,其內容是正確的,但為什麼用相同鍵值去找,結果竟然回傳查無資料呢?原來問題就出在以字元串列常數當索引鍵值。

由於我所開發的這個程式,它接受許多不同格式的資料,各種格式所使用的欄位名稱也都不同。為了設計的簡單化,同人把拆解格式與對映的工作分開來處理,並且把拆解與對映的資料都存放在 map 容器中,而不去設計存放資料物件的類別,以免日後受到欄位規格變動的影響而大動程式碼。

考慮資料處理的效率與易讀性,同人將容器的鍵值設為字元串列常數(const char*)。程式剖析資料的結果,以測試程式確認容器內容無誤,但等到實際呼叫此程式卻發現,無法以容器內含相同鍵值來取得所需要的資料。

怎麼會這樣呢?用走訪 map 容器的方式傾印容器內含資料沒問題,但直接用 find () 取得資料卻得到的是 map::end ()。同人不信邪,用走訪所使用的迭代器所指向的鍵值來 find (),結果可以正確無誤找到資料。這顯示著直接 find () 和用迭代器指向的鍵值兩者並不一樣,但同樣是 const char*,內容值又一模一樣,兩者應該是相同的才對呀!

而且,如果是用 const char* 當索引鍵值有問題,那麼為什麼以同樣的容器操作模式,在拆解格式的處理沒有問題,而卻會在欄位對映時出現錯誤呢?比較兩者的差異,我想我知道問題出在什麼地方了。我試著以整數來取代字元串列常數來當索引鍵值,反正雖然各種格式欄位互異,但最後對應到的欄位都是相同的。果然,找不到資料的問題就不再出現了。

其實字元串列鍵值的比較是基於資料存放位址而非資料內含值,只是常數變數編譯器會幫我們把相同內容的字元串列放在一起。但由於同人並不是用宣告 const 常數而是用 #define 的方式來定義常數,對於不同程式碼的相同字元串列常數,可能編譯器並不會將他們視會相同的變數,結果就會造成比對不同而無法找到資料了。

因此,用整數來取代字元串列常數當索引鍵值就可以避免以上的問題,而且也省下用來存放字元串列常數的空間,但卻有數值代碼代表意義不明而造成維護的問題。針對這個問題我們可以宣告 enum 變數來解決,但其索引鍵值還是應該用 unsigned int 而不能使用 enum 型態。為什麼呢?因為 enumequality comparable 而不是 less than comparable,而 mapassociative container,其鍵值必須是 less than comparable 呀。

Please follow and like us:
分類: 問題解決, 編程技巧, 職場。這篇內容的永久連結

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *