2019年5月24日 星期五

implement itoa()

itoa() 並不是 c 標準, 有些平台有支援, 但 gnu c 並沒有支援這個。

而在 bare-metal 環境會很需要把整數轉成字串, 搭配 print 之類的函式來除錯; itoa 實作並不難, 這裡有個範例可以抄。

http://www.retro11.de/ouxr/43bsd/usr/ingres/source/gutil/itoa.c.html

不過有個討厭的數學問題:
1 *j-- = i % 10 + '0';
2 i /= 10;

i%10 如果 i 是負數會怎麼樣, 以下的 c++ 程式碼會印出什麼?

cout << (-8) % 16 << endl;

在我的平台是 -8, 但他不是一個固定答案, 根據不同語言的實作有不同的結果。而如果是 -8 的話, 會造成取到錯誤的 array index, 程式不至於會當掉, 但會得到錯誤結果。

ref:
负数究竟是如何取模的?

很久之前就知道這問題, 不過當時這並不是太重要的問題, 我一直沒理他, 現在學習告個段落, 該是好好正視這問題。

所以改善的作法是不要用負數取餘數, 改用正整數來取餘數。

目標是這樣:
我希望傳入 -1 時, 可以得到 -1 的字串; 傳入 0x80000000 可以得到 80000000 的字串。

itoa test
183   //int v = 0x80000000;
184   int v = -1;
185   char str[32];
186   itoa_32(v, str, 16);
187   printf("str: %s\n", str);
188
189   itoa_32(v, str, 10);
190   printf("str: %s\n", str);
191
192   return 0;

數字: -1
str: FFFFFFFF (16 進位)
str: -1       (10 進位)

數字: 0x80000000
str: 80000000    (16 進位)
str: -2147483648 (10 進位)

以下是最後修改的版本:
itoa.c
 2 char* itoa_32(int n, char* str, int radix)
 3 {
 4   char digit[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 5   char* p=str;
 6   char* head=str;
 7   unsigned int val;

12   if (n==0)
13   {
14     *p++='0';
15     *p=0;
16     return str;
17   }
18   if (radix == 10 && n < 0)
19   {
20     val = (-n);
21   }
22   else
23     val = n;
24 
25   while(val)
26   {
27     *p++=digit[val%radix];
28     val/=radix;
29   }
30 
31   if (radix == 10 && n < 0)
32     *p++='-';
33 
34   *p=0;
36   for (--p; head < p ; ++head, --p)
37   {
38     char temp=*head;
39     *head=*p;
40     *p=temp;
41   }
43   return str;
44 }
45 

itoa.c L27 一律用 val 這個 unsigned int 來取餘數。

這個 itoa 還有其他問題, 例如沒有檢查 str 的大小, 是有可能超過邊界的, 不過由於知道 int 最大是 11 個位數 (包涵負號), 把 str array 設定大一點, 勉強可以避開這問題。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。