剛剛追一個 bug,結果發現問題出在 atoi(),果然還是要用 C++ 才會比較有 type-safety,要不然碰到這類問題,連自己死了都不知道。

事情是這樣子的。在一組透過 HTTP 溝通的網路程式裡,其中一端把某個型別為 uint32_t 的參數,用 sprintf() 轉成文字形式,附在 URL 的 QUERY_STRING 裡,傳送到另一端,接收端收到後,從 URL 裡 parse 出文字形式的這個參數,丟給 atoi() 轉回 uint32_t。整個流程大概如下圖:

uint32_t ID = 0x9fffffff;              uint32_t ID = 0x7fffffff;
            |                                      ^
            |                                      |
sprintf(qstr, "%u", ID);                  atoi("2684354559");
            |                                      |
            v                                      |
       "2684354559"                          "2684354559"
            |                                      ^
            |                                      |
            +--(append to URL, pass via Internet)--+

問題出在 atoi() 這個函式,其回傳值是個 int。所以只要這個數字大於 INT_MAX,也就是 0x7FFFFFFF,就一定會回傳 0x7FFFFFFF。於是,這個數字就被改掉了,不正確的結果,導致程式其他部份行為不正常。

解法有兩種:使用 std::istringstream,或是 sscanf(),這兩個都可以反應型別的不同,只不過前者可以自動反應,後者需手動使用 "%u" 格式字串。

如果嫌這兩個方法,寫起來又臭又長,也可以使用 Boost 的 lexical_cast。不過這個一樣得寫明輸出型別為 uint32_t,但比 "%u" 好懂許多。不曉得在 C++0x 裡,可不可以利用 auto,寫成下面那樣,會比較方便:

ID = lexical_cast<auto>("2684354559");

如果 auto 能夠省去,那就更方便了。