🔤 C5-3 字符串模拟与输出调试

知识点体系逻辑图 · 陶老师 C++ 编程课堂

🎯 字符串模拟与输出调试
C5模拟法系列第三讲 · 四大知识模块
📝

模块一:字符串操作函数

  • s.size():获取字符串长度
  • s[i]:访问第i个字符(从0开始)
  • s.substr(i, len):截取子串
  • s.replace(i, len, str):替换子串
  • s.find(p):查找子串位置
🔍

模块二:字符串上的模拟

  • 有重叠子串计数:找到后i++继续找
  • 无重叠子串计数:找到后i+=m跳过
  • 开心消消乐:replace消除连续相同字符
  • 字符遍历:for循环逐个处理字符
🔧

模块三:语法问题与编译错误

  • expected '}':缺少右括号
  • not declared in scope:变量未声明/缺头文件
  • 错误定位特点:报错位置可能不准
  • 配对练习:错误信息↔错误原因
🐛

模块四:逻辑错误与输出调试

  • 编译通过≠正确:逻辑错误运行才暴露
  • 输出调试法:cout打印中间变量
  • 质数的和:调试循环范围
  • 隐藏敏感信息:find+replace模拟

📌 核心语法速查

// 字符串操作四件套 #include <iostream> #include <string> using namespace std; int main() { string s = "hello"; cout << s.size() << endl; // 5 cout << s[0] << endl; // h cout << s.substr(0,3) << endl; // hel s.replace(0, 3, "HE"); cout << s << endl; // HElo return 0; }

⚖️ 有重叠 vs 无重叠 对比

对比项有重叠计数无重叠计数
找到一个子串后i++(继续下一位)i += m(跳过m位)
s="aaaa", p="aa"结果=3结果=2
循环写法for正常i++for+手动i+=m
典型应用统计出现次数消消乐、替换

🔄 字符串模拟解题流程

第1步:明确模拟目标(计数?替换?消除?)
↓
第2步:确定遍历方式(逐个字符扫描 or 滑动窗口)
↓
第3步:用substr截取窗口子串进行比较
↓
第4步:根据规则更新(计数++ / replace替换 / i+=m跳过)
↓
第5步:输出结果,用cout调试中间步骤

⚠️ 高频易错点总结

① substr 第二个参数是"长度"不是"结束位置"
错误现象:写 s.substr(0, 5) 期望得到"前5个字符到位置5",结果截取的是从位置0开始的5个字符。
原因分析:substr(起始位置, 长度),第二个参数是截取的字符个数,不是结束下标。学生容易和Python切片 s[0:5] 混淆。
正确做法:牢记 substr(i, len) = 从位置i开始,截取len个字符。如果想截到末尾,只传一个参数:s.substr(i)。
② 有重叠/无重叠计数时 i 的更新写错
错误现象:s="aaaa", p="aa",有重叠应该得3,结果只输出2;或者无重叠应该得2,结果输出3。
原因分析:有重叠时找到子串后忘记i++(或误用了i+=m);无重叠时找到后只cnt++没有i+=m跳步。两种场景的i更新策略记反了。
正确做法:有重叠→for循环正常i++,找到只cnt++;无重叠→找到后必须 i += m 跳过m个字符,否则 i++。用表格对比记忆!
③ replace 后字符串长度变化导致索引错位
错误现象:执行 s.replace(0, 3, "XX") 后,再访问 s[3] 得到的不是预期字符,甚至越界崩溃。
原因分析:replace 把原来3个字符替换成2个字符,字符串长度变短了!后续所有下标都往前移了1位,但代码还按原来的下标访问。
正确做法:replace 之后,立刻意识到字符串长度可能变了!如果还要继续操作,重新计算长度或从 replace 的位置重新开始扫描(消消乐题就是这个套路)。
④ 字符串下标越界(循环边界写错)
错误现象:运行程序突然崩溃(Segmentation Fault),或者输出乱码字符。
原因分析:循环条件写成 i <= s.size() 或 i < s.size() - 1,多访问了一位或少访问了一位。字符串下标从0开始,最后一个字符下标是 s.size()-1。
正确做法:遍历字符串用 for(int i = 0; i < s.size(); i++),左闭右开,天然不会越界。截取子串时检查 i + m <= s.size()。
⑤ 编译报错行号不对,实际错误在前面
错误现象:编译器报第19行 expected '}' at end of input,但去看第19行根本没问题。
原因分析:C++编译器是逐行扫描的,遇到括号不配对时,它往往要到文件末尾才发现"少了一个}",所以报错位置可能比真实错误位置靠后很多。
正确做法:看到报错行号时,先往前面找!尤其是括号配对类错误(expected '}'),真实错误通常在报错行往上数几行。学会从报错信息中提取关键关键词。
⑥ 逻辑错误时不会用 cout 输出调试
错误现象:编译通过了,但运行结果不对,盯着代码看半天也找不出问题在哪。
原因分析:不会用输出调试法,不知道该在哪打印中间变量。或者一次改了好几个地方,改完还是不知道哪个改动生效了。
正确做法:在循环里加 cout << "调试: i=" << i << " s=" << s << endl;,看哪一步开始偏离预期。一次只改一个地方,改完立刻运行对比。
⑦ char 用双引号,string 用单引号
错误现象:写 char c = "a"; 或 string s = 'hello';,编译报错或结果异常。
原因分析:单引号 'a' 表示一个字符(char类型),双引号 "a" 表示一个字符串(const char*类型)。学生经常混用引号。
正确做法:char 用单引号:char c = 'a';;string 用双引号:string s = "hello";。一个字符=单引号,一串字符=双引号!
⑧ s.size() 返回无符号数,与 int 比较有坑
错误现象:写 for(int i = s.size() - 1; i >= 0; i--) 倒序遍历,结果死循环或直接跳过。
原因分析:s.size() 返回的是 unsigned int(无符号数),永远 >= 0。当 i 减到 -1 时,和无符号数比较会被隐式转换成巨大正数,循环停不下来。
正确做法:倒序遍历时,先把长度存到 int 变量:int n = s.size(); for(int i = n - 1; i >= 0; i--)。或者正序遍历更安全:for(int i = 0; i < (int)s.size(); i++)。