递归形式的itoa及reverse函数的实现

本文介绍如何使用C语言编写递归版本的itoa和reverse函数,用于整数到字符串的转换及字符串反转。文章详细展示了函数实现过程,并分析了递归函数中静态局部变量的作用和初始化细节。

     源自《The C Programming Language》P75 pr4-12, 4-13:

     

     编写递归版本的itoa及reverse函数

 

     代码:

    

     分析:

 

     1,  itoa函数中,将局部变量i定义为静态局部变量,这样当递归调用itoa函数时,不会重复的定义变量i了,例如:第一次

          调用itoa函数,定义了static int i; , 这样第二次调用itoa函数时,就不会再定义变量i了,而是直接使用第一次itoa函数

          中的i的值

 

     2,  reverse函数这样定义的目的:保持reverse函数的用户接口一致,即只需要传递一个参数(待处理的字符串)

 

     3,  而参考函数reverser的实现方式比自己实现的reverse函数要好,我编写的reverse函数中使用到全局变量beg,end,

          这样安全性不高,内聚度也不高;反之参考的reverser函数将变量beg,end作为reverser的形参,这样就不存在上述

          问题了,并且作为形参的另一个好处:如果不作为形参,而是在reverser定义beg,end,这样,又必须将其定义为

          静态局部变量,并且因为静态局部变量的初始化形式必须是常量表达式,如果定义成static int end = strlen(s) - 1;

          形式将出错,而不这样定义的话,那又无法使用静态局部变量beg, end

 

     4,  在递归函数中定义的静态局部变量的初始化时,注意:

          正确:

          void fun()

          {

                  static int n = 2;

                   ...

                   fun();

          }

          如上所示的定义并初始化方式是正确的,并符合设计者的思路;

          错误:

          void fun()

          {

                  static int n;

                  n = 2;

                   ...

                  fun();

          }

          这种定义方式编译器虽然不会报错,并且也能运行,但逻辑上有问题,不能达到设计者的要求,

          虽然n是static int 型变量,但每次fun函数递归调用自己时,n的值并不能保持上次的值,因为

          在每次进入fun函数后,n的值都会被赋予2,这样定义的静态局部变量n就没有任何意义了

 

 

 

 

 

 

# 函数详解(C语言) 下面是对题目中提到的11个函数的详细讲解,包括其功能、算法思路、使用方法及注意事项。 --- ## **函数一:十六进制转十进制** ```c int hexToDec(char *hex) ``` ### 功能 将输入的十六进制字符串(如 `"A1"`)转换为对应的十进制整数(如 `161`)。 ### 使用方式 ```c int num = hexToDec("FF"); // 返回 255 ``` ### 方法 - 使用标准库函数 `sscanf(hex, "%x", &num)` 自动解析; - 或手动遍历每一位字符,按权展开: $$ \text{value} = \sum_{i=0}^{n-1} d_i \times 16^{n-1-i} $$ ### 示例 | 十六进制 | 十进制 | |---------|-------| | A | 10 | | FF | 255 | | 100 | 256 | > 注意:字母不区分大小写(`a~f` 等价于 `A~F`) --- ## **函数二:判断素数** ```c int isPrime(int n) ``` ### 功能 判断一个正整数是否为**素数**(只能被1和自身整除的大于1的自然数)。 ### 使用方式 ```c if (isPrime(17)) printf("Y\n"); else printf("N\n"); // 输出 Y ``` ### 算法思路 1. 若 $ n \leq 1 $,不是素数; 2. 若 $ n = 2 $,是素数; 3. 若 $ n $ 是偶数且大于2,不是素数; 4. 否则从 $ i=3 $ 开始,只检查奇数因子直到 $ \sqrt{n} $。 ### 时间复杂度 $$ O(\sqrt{n}) $$ ### 示例 | 输入 | 是否素数 | |------|----------| | 2 | Y | | 9 | N | | 23 | Y | --- ## **函数三:最大公约数(GCD)** ```c int gcd(int a, int b) ``` ### 功能 求两个整数的最大公约数(Greatest Common Divisor),即能同时整除它们的最大正整数。 ### 使用方式 ```c int g = gcd(12, 18); // 返回 6 ``` ### 算法:辗转相除法(欧几里得算法) ```c while (b != 0) { int temp = b; b = a % b; a = temp; } return a; ``` ### 公式 $$ \gcd(a, b) = \gcd(b, a \mod b) $$ ### 示例 | a | b | gcd | |----|----|-----| | 12 | 18 | 6 | | 15 | 25 | 5 | --- ## **函数四:最小公倍数(LCM)** ```c int lcm(int a, int b) ``` ### 功能 求两个整数的最小公倍数(Least Common Multiple),即能被两者整除的最小正整数。 ### 使用方式 ```c int l = lcm(4, 6); // 返回 12 ``` ### 公式 $$ \text{lcm}(a, b) = \frac{a \times b}{\gcd(a, b)} $$ ### 注意事项 - 先计算 GCD 再代入公式; - 防止溢出:建议先除后乘(`a / gcd(a,b) * b`); ### 示例 | a | b | lcm | |----|----|-----| | 4 | 6 | 12 | | 7 | 5 | 35 | --- ## **函数五:冒泡排序(从小到大)** ```c void bubbleSort(int arr[], int n) ``` ### 功能 将数组中的元素按升序排列。 ### 使用方式 ```c int data[] = {5, 2, 8, 1}; bubbleSort(data, 4); // 排序后变为 {1, 2, 5, 8} ``` ### 原理 - 多轮比较相邻元素; - 每轮将最大值“浮”到最后; - 重复 $ n-1 $ 轮。 ### 代码逻辑 ```c for (int i = 0; i < n - 1; i++) for (int j = 0; j < n - i - 1; j++) if (arr[j] > arr[j+1]) swap(arr[j], arr[j+1]); ``` ### 时间复杂度 $$ O(n^2) $$ --- ## **函数六:3×3数组转置** ```c void transpose(int mat[3][3]) ``` ### 功能 将矩阵的行与列互换。即 $ \text{mat}[i][j] \leftrightarrow \text{mat}[j][i] $ ### 使用方式 ```c int mat[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; transpose(mat); // 转置后第一行为 1 4 7 ``` ### 原理 - 只需交换上三角与下三角; - 循环控制:`for j = i+1 to 2` ### 示例 原始: ``` 1 2 3 4 5 6 7 8 9 ``` 转置后: ``` 1 4 7 2 5 8 3 6 9 ``` --- ## **函数七:输出3×3数组** ```c void printMatrix(int mat[3][3]) ``` ### 功能 以矩阵形式打印二维数组,每行三个数,空格分隔。 ### 使用方式 ```c printMatrix(mat); // 按行输出每个元素 ``` ### 实现 ```c for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) printf("%d ", mat[i][j]); printf("\n"); } ``` --- ## **函数八:计算方差** ```c double variance(int arr[], int n) ``` ### 功能 计算一组数据的**方差**(Variance),反映数据离散程度。 ### 使用方式 ```c double var = variance(nums, 10); // 计算10个数的方差 printf("%.2f\n", var); ``` ### 公式 $$ \text{Var} = \frac{1}{n} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$ 其中 $\bar{x}$ 是平均值。 ### 步骤 1. 求平均值; 2. 对每个数求偏差平方; 3. 求这些平方的平均值。 ### 示例 数据:`{1, 2, 3}` → 平均值=2 → 方差 = $(1+0+1)/3 ≈ 0.67$ --- ## **函数九:递归整数转字符串** ```c void intToString(int n, char str[]) ``` ### 功能 将整数 $ n $ 转换为其对应的字符串形式(如 `123 → "123"`),使用**递归**实现。 ### 使用方式 ```c char s[20]; intToString(123, s); // s 变为 "123" ``` ### 递归思想 - 分解最高位以外的部分 → 递归进入; - 最低位单独处理; - 利用函数返回过程逐位写入字符。 ### 关键点 - 使用 `static int index` 记录当前写入位置; - 递归到底后再开始赋值; - 必须处理 `n=0` 特殊情况。 ⚠️ 缺陷:静态变量无法多次调用,实际推荐改写为非静态版本(见 `itoa_safe`)。 --- ## **函数十:字符串反序存放** ```c void reverseString(char str[]) ``` ### 功能 将字符串内容倒过来(如 `"abc"` → `"cba"`)。 ### 使用方式 ```c char s[] = "hello"; reverseString(s); // s 变为 "olleh" ``` ### 方法:双指针法 ```c int left = 0, right = strlen(str) - 1; while (left < right) { swap(str[left], str[right]); left++; right--; } ``` ### 注意 - 不改变字符串长度; - 保留 `\0` 在末尾; - 适用于回文判断、反转输出等场景。 --- ## **函数十一:字符串连接** ```c void strcat_safe(char dest[], const char *src) ``` ### 功能 将源字符串 `src` 追加到目标字符串 `dest` 末尾。 ### 使用方式 ```c char dest[50] = "Hello"; strcat_safe(dest, "World"); // dest 变为 "HelloWorld" ``` ### 实现 1. 找到 `dest` 的末尾(`\0` 位置); 2. 将 `src` 中每个字符复制过去; 3. 添加新的 `\0`。 ### 示例 ```c char res[50] = ""; strcat_safe(res, "123"); strcat_safe(res, "456"); // res = "123456" ``` 用于题目中“两两连接”操作。 --- # 总结表格 | 函数 | 名称 | 输入 | 输出 | 关键技术 | |------|------|------|------|--------| | 一 | hexToDec | 十六进制字符串 | 十进制整数 | `sscanf` 或权展开 | | 二 | isPrime | 整数 | Y/N(布尔) | 枚举因子至 √n | | 三 | gcd | 两整数 | 最大公约数 | 欧几里得算法 | | 四 | lcm | 两整数 | 最小公倍数 | $ ab / \gcd(a,b) $ | | 五 | bubbleSort | 数组 | 排序后数组 | 相邻比较交换 | | 六 | transpose | 3×3矩阵 | 转置矩阵 | 行列互换 | | 七 | printMatrix | 3×3矩阵 | 打印输出 | 二维循环 | | 八 | variance | 数组 | 浮点方差 | 偏差平方平均 | | 九 | intToString | 整数 | 字符串 | 递归拆位 | | 十 | reverseString | 字符串 | 反序字符串 | 双指针交换 | | 十一 | strcat_safe | 两字符串 | 拼接结果 | 定位末尾追加 | --- > ✅ 提示:在实际编程中,注意数组越界、字符串结尾 `\0`、静态变量冲突等问题。建议调试时逐步测试每个函数
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值