Melon Playground甜瓜游乐场
98.73M · 2026-03-22
本文承接给定代码的核心需求(堆内存申请 + 递归求字符串长度),先指出原代码的核心问题,再提供3 种不同思路的递归实现方案,同时完善程序的健壮性,帮你吃透递归的核心逻辑与字符串操作的细节。
先明确原递归函数fnc的关键错误,避免踩坑:
*a == ' '(字符串结束符)时,说明已经遍历到字符串末尾,该位置不计入有效长度,应返回0而非1,原代码会导致计算结果比实际长度多 1;fnc(a+1)+1的逻辑本身可行(后续遍历结果 + 当前字符),但因终止条件错误,最终结果失真;scanf("%s", a)无法读取含空格的字符串,可保留该写法(符合题目要求),同时补充说明替代方案。先搭建通用的程序框架,堆内存的申请、判断、释放是固定规范,所有递归方案都基于此框架运行:
c
运行
/******************************
*文件名称:2.String_length.c
*作者:czy
*邮箱:caozhiyang_0613@163.com
*创建日期:2026/1/4
*修改日期:2026/01/11
*文件功能:堆内存存储字符串,多种递归方案计算字符串实际长度
*****************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// 后续递归函数声明(根据不同方案替换)
int str_len_recur1(char *a);
int str_len_recur2(char *a);
int str_len_recur3(char *a, int count);
int main()
{
char *a = NULL;
int len = 0;
// 1. 申请堆内存(大小100字节,足够存储常规输入字符串)
a = (char *)malloc(100 * sizeof(char));
if(a == NULL) // 健壮性判断:内存分配失败直接退出
{
printf("内存分配失败!n");
return 1;
}
// 2. 键盘输入字符串(scanf("%s"):不支持空格,符合题目要求)
printf("请输入一串字符(不含空格):n");
scanf("%s", a);
// 3. 调用递归函数计算长度(可切换不同递归方案)
len = str_len_recur1(a); // 方案1
// len = str_len_recur2(a); // 方案2
// len = str_len_recur3(a, 0); // 方案3
// 4. 输出结果
printf("字符串的实际长度为:%dn", len);
// 5. 释放堆内存,避免内存泄漏
free(a);
a = NULL; // 置空避免野指针
return 0;
}
这是最简洁、最常用的递归方案,修正原代码的错误,核心是 “指针后移遍历,遇到结束符返回 0,否则后续结果 + 1”。
c
运行
// 方案1:基础递归(无额外参数,指针偏移遍历)
int str_len_recur1(char *a)
{
// 终止条件:遇到字符串结束符' ',返回0(不计入长度)
if(*a == ' ')
{
return 0;
}
// 递归逻辑:当前字符有效(+1),继续遍历下一个字符(a+1)
else
{
return str_len_recur1(a + 1) + 1;
}
}
str_len_recur1("abc"):*a='a'≠' ',返回str_len_recur1("bc")+1;str_len_recur1("bc"):*a='b'≠' ',返回str_len_recur1("c")+1;str_len_recur1("c"):*a='c'≠' ',返回str_len_recur1("")+1;str_len_recur1(""):*a=' ',返回0;0+1+1+1=3,最终结果为 3(正确)。该方案引入下标变量i,通过 “指针 + 下标” 的方式遍历字符串,递归的核心是 “下标递增,直到对应字符为 ' '”,适合习惯数组下标操作的开发者。
c
运行
// 方案2:递归+下标索引(封装辅助函数,对外接口简洁)
// 对外接口:仅需传入字符串指针
int str_len_recur2(char *a)
{
// 辅助递归函数:传入字符串和当前下标
int helper(char *a, int i);
return helper(a, 0); // 下标从0开始遍历
}
// 辅助递归函数(内部使用,处理下标递增)
int helper(char *a, int i)
{
// 终止条件:下标i对应的字符是' ',返回0
if(a[i] == ' ')
{
return 0;
}
// 递归逻辑:当前下标字符有效(+1),下标i+1继续遍历
return helper(a, i + 1) + 1;
}
0+1+1+1=3,计算准确。该方案引入累加器count,用于记录当前已遍历的有效字符数,递归的核心是 “遍历到下一个字符,累加器 + 1,直到遇到 ' ' 返回累加器”,属于尾递归(递归调用在函数最后一步,无后续计算),部分编译器可对其进行优化,避免栈溢出。
c
运行
// 方案3:递归+累加器(尾递归,带计数参数)
int str_len_recur3(char *a, int count)
{
// 终止条件:遇到' ',返回累加器count(已记录的有效字符数)
if(*a == ' ')
{
return count;
}
// 尾递归逻辑:指针后移(a+1),累加器+1,无后续回溯计算
return str_len_recur3(a + 1, count + 1);
}
0:len = str_len_recur3(a, 0);str_len_recur3("abc", 0):*a='a'≠' ',调用str_len_recur3("bc", 1);str_len_recur3("bc", 1):*a='b'≠' ',调用str_len_recur3("c", 2);str_len_recur3("c", 2):*a='c'≠' ',调用str_len_recur3("", 3);str_len_recur3("", 3):*a=' ',返回3(直接返回累加器,无需回溯);plaintext
请输入一串字符(不含空格):
hello
字符串的实际长度为:5
plaintext
请输入一串字符(不含空格):
Clanguage
字符串的实际长度为:9
plaintext
请输入一串字符(不含空格):
1234567890
字符串的实际长度为:10
堆内存操作规范:malloc申请→NULL判断→使用→free释放→NULL置空,五步缺一不可,避免内存泄漏和野指针;
递归求字符串长度的核心:
*a == ' '(或a[i] == ' '),返回值根据方案调整(0 或累加器);a+1),要么下标递增(i+1),确保遍历所有有效字符;3 种递归方案的对比:
| 递归方案 | 优点 | 缺点 |
|---|---|---|
| 方案 1(基础指针偏移) | 简洁高效,无额外参数,易理解 | 非尾递归,长字符串可能栈溢出 |
| 方案 2(下标索引) | 贴近数组操作习惯,可读性强 | 需封装辅助函数,略繁琐 |
| 方案 3(尾递归 + 累加器) | 无回溯计算,效率高,栈帧占用少 | 调用时需传入初始累加器,接口不够简洁 |
字符串操作细节:scanf("%s", a)遇空格终止,若需读取含空格的字符串,可替换为fgets(a, 100, stdin),并配合换行符处理函数。
scanf替换为fgets,并添加换行符处理函数,避免换行符被计入长度;realloc调整堆内存大小,避免内存浪费;掌握这 3 种递归方案,不仅能解决字符串长度计算问题,还能举一反三,将递归思想应用到链表遍历、树的遍历等更多场景中。递归的核心是 “分解问题 + 终止条件”,只要抓住这两点,就能写出高效、正确的递归代码。
Stream 进阶三部曲:flatMap、reduce 与并行流的实战陷阱与最佳实践
【超分实战】拒绝灾难性遗忘!记一次原生4K医疗影像(SurgiSR4K)的模型微调踩坑实录
2026-03-22
2026-03-22