代码改变世界

itoa函数的递归实现(二级指针实现)

2015-09-14 15:34  OshynSong  阅读(1114)  评论(5编辑  收藏  举报

问题提出

《C Programming Language》书中在递归这一节预留了两个使用递归实现的函数,其中itoa函数是用来将一个整数转换为一个字符串。书中已有使用循环实现的版本,但是直接得到的是反序的结果,需要最后调用reverse函数。而递归版本则可以避免这个问题。 
首先使用原接口void itoa(int n, char s[])进行实现,发现递归调用的时候总是错误,输出的结果只能得到整数n的最高位和最低位的数字。代码如下:

 1 void itoa(int n, char* s)
 2 {
 3     if (n < 0)
 4     {
 5         *s = '-';
 6         s++;
 7     }
 8     if (abs(n) / 10)
 9         itoa(abs(n) / 10, s++);
10     *s = abs(n) % 10 + '0';
11 }

基本思想:使用字符指针s进行移动,递归调用的过程中进行指针的自增。但是使用gcc编译后运行结果并不正确。只能输出整数n的最高位和最低位的两个数字。 
错误原因:经过仔细调试,发现字符指针参数每次递归调用时,传入的s++这个变量时进行了复制,也就是在后续的递归调用中对指针s的自增操作不会反映到第一次调用的指针s上去,因此,第一次调用这个函数并最终调用*s = abs(n) % 10 + '0';这一语句时,指针s指向的是字符数组的第二个位置。就将整数n的最低位放在第二个位置,而整数n的最高位一直进行递归调用并将参数指针变量复制(一直指向第一个位置),也就放在了第一个位置。最后返回的结果就只有两个数字。

已有的方案

针对上述遇到的问题,在网上搜索了这个问题。使用最多的就是使用局部静态变量保存字符数组的下标,参考http://blog.csdn.net/long_xing/article/details/2212450。但是,另外一篇博客中的实现存在逻辑错误,提前给字符数组赋“\0”,参见http://blog.csdn.net/roma823/article/details/6546719

自己实现

经过一段时间的思考,最终想到的办法就是二级字符指针,因为使用字符指针函数参数时,递归调用传入的字符指针使用了复制,这应该是编译器对字符指针(相当于字符串),在进行传递参数时使用复制指针(也就是复制字符串)这种实现,与普通意义上的指针参数调用时直接操作源指针的思想有所区别。因此在使用一个二级字符指针,每次传递的是指向字符串的同一个指针,这样就不会出现递归调用过程中对指针的自增操作对上一级调用函数无效的情况,代码如下:

 1 void itoa(int num, char** s)
 2 {
 3     if (num < 0)
 4     {
 5         **s = '-';
 6         (*s)++;
 7     }
 8     if (abs(num / 10) > 0)
 9     {
10         itoa(abs(num / 10), s);
11         (*s)++;
12     }
13     **s = abs(num % 10) + '0';
14 }

上述版本使用了abs库函数,可以转换最大负整数。测试如下:

 1 int main()
 2 {
 3     char s[100], *ps = s;
 4     itoa(12345, &ps);   //测试正整数
 5     *(++ps) = '\0';
 6     printf("%s\n", s);
 7 
 8     ps = s;
 9     itoa(-2147483647, &ps);  //测试最大的负整数(32 bits)
10     *(++ps) = '\0';
11     printf("%s\n", s);
12 
13     return 0;
14 }