C语言字符串逆序反转详解:双指针与非递归/递归实现
在C语言开发中,字符串逆序反转是考察指针与内存操作的经典场景。通过双指针从两端向中间逼近并交换元素,可以高效实现原地翻转。本文将对比非递归的双指针遍历法与递归调用法,重点剖析形参传值导致的长度计算差异及栈帧管理机制。
双指针法核心逻辑
字符串逆序的本质是将首尾字符进行配对交换。通过定义两个指针(或下标),一个指向头部,另一个指向尾部,在满足条件时执行交换操作并向中心移动,直至相遇。
第1步:基于指针的直接交换
目的:利用指针直接操作内存地址,演示最基础的逆序逻辑。
入口:包含 <stdio.h> 和 <string.h>。
动作:声明字符数组,获取长度后设置首尾指针进行 while 循环交换。
#include<stdio.h>
#include<string.h>
void rec(char* arr)
{
int len = strlen(arr);
printf("字符串长度len = %d\n", len);
char* p1 = arr; // 首指针
char* p2 = &arr[len - 1]; // 尾指针
while (p1 < p2)
{
char temp = *p1;
*p1 = *p2; // 正向赋值
*p2 = temp; // 逆向回写
p1++; // 指针后移
p2--; // 指针前移
}
}
int main()
{
char arr[] = "hello world";
rec(arr);
printf("逆序之后输出为:%s\n", arr);
return 0;
}
完成判断:执行后原字符串内容被改变为倒序排列。注意:因为传入的是内存地址,修改会直接影响原始数据,若需保护源数据应先拷贝一份。
字符串逆序的常见实现方式
在实际编程中,通常使用数组下标替代指针进行操作,或者将逻辑封装为函数。需注意区分“逆序存储”与“逆序打印”,前者改变内存中的 str[0] 到 str[n-1],后者仅调整输出顺序。
第2步:非递归下标遍历
准备:定义定长字符数组或使用动态输入。对于用户输入的变长字符串,必须预留足够空间如 char arr[101] 防止越界。
动作:
- 计算有效长度:输入静态常量时使用
sizeof(arr)/sizeof(arr[0])-1;读取输入后务必使用strlen(arr)。 - 设定左右索引:`left = 0`, `right = len - 1`。
- 执行循环:当
left < right时交换arr[left]与arr[right]并步进索引。
#include <stdio.h>
#include <string.h>
// 以固定数组为例
int main()
{
char arr[] = "abcdef";
int sz = sizeof(arr) / sizeof(arr[0]); // 包含 '\0',总数6个
int left = 0;
int right = sz - 2; // 减2避开末尾的结束符 '\0'
while (left < right)
{
char tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
printf("%s", arr); // 结果 fedcba
return 0;
}第3步:封装为函数时的避坑指南
当将逆序逻辑封装为 `void reverse(char arr[])` 时,最大的难点在于计算长度。
- 错误做法:直接使用 `sizeof(arr)/sizeof(arr[0])`。因为在函数内部,数组名退化为指针,其大小仅为4字节(32位)或8字节(64位)。这会导致计算结果为0或1,无法正确运行。
- 正确做法:始终使用
strlen(arr)获取字符串的实际字符数。在函数内,形参char arr[]本质等价于char *arr。
// 正确的函数封装示例
void reverse(char arr[])
{
int left = 0;
int right = strlen(arr) - 1; // 只能用库函数求长度
while (left < right)
{
char tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
递归实现字符串逆序
递归解法利用函数调用栈的特性,层层深入并在回溯过程中恢复元素位置。以下是两种典型的递归思路。
第4步:方法一 —— 截断递归法
原理:每次递归都将当前最后一个字符暂存,并将字符串末尾强制置零,从而形成一个短一截的新子串进行递归。待子串处理完毕后再回溯,将暂存的字符填回末尾。
- 保存首位字符到临时变量。
- 将末位字符移到首位。
- 将末位覆盖为
'\0',缩小问题规模。 - 对剩余部分递归调用。
- 在回溯时,将暂存的首位字符填入末位。
#include <stdio.h>
#include <string.h>
void reverse(char str[])
{
char tmp = *str;
int len = strlen(str);
if (len == 0) return;
*str = *(str + len - 1);
*(str + len - 1) = '\0';
if (strlen(str + 1) >= 2) // 中间元素无需交换
{
reverse(str + 1);
}
*(str + len - 1) = tmp; // 回溯还原
}
int main()
{
char arr[101] = { 0 };
scanf("%s", arr);
reverse(arr);
printf("%s\n", arr);
return 0;
}
第5步:方法二 —— 指针范围递归法
原理:更直观地传递左右边界参数,符合迭代法的思维,但在递归框架下执行。
- 接收左边界 `left` 和右边界 `right`。
- 交换两端的值。
- 以
left+1和right-1为参数继续递归。 - 基准条件:`left >= right` 时停止。
#include <stdio.h>
#include <string.h>
void reverse(char str[], int left, int right)
{
if (left < right)
{
char tmp = str[left];
str[left] = str[right];
str[right] = tmp;
reverse(str, left + 1, right - 1); // 递归缩减范围
}
}
int main()
{
char arr[101] = { 0 };
scanf("%s", arr);
reverse(arr, 0, strlen(arr)-1);
printf("%s\n", arr);
return 0;
}
总结与注意事项
- 长度计算的陷阱:仅在静态初始化的代码块中使用 `sizeof` 计算有效长度安全,进入函数后务必改用 `strlen`。
- 数组定义:用于接收键盘输入时,字符数组必须指定明确的大小(如
[101]),否则极易发生缓冲区溢出。 - 内存保护:所有上述例子均为原地修改(In-place)。如果需要保留原字符串,请先调用
strcpy创建副本。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













