C++连续字符统计代码输出多了额外‘1’,求问题原因
嘿,我来帮你揪出这个多余的‘1’到底是怎么冒出来的!
你预期输出是2a3b3c4d3l4z,但实际得到12a3b3c4d3l4z,核心问题出在字符串长度的计算和循环边界处理上,咱们一步步拆解:
问题1:错误使用sizeof获取字符串长度
你定义的char ipstr[] = "aabbbcccddddzzzzlll";是C风格字符串,它在内存里会自动在末尾添加一个空终止符\0来标记字符串结束。sizeof(ipstr)返回的是整个数组的字节数,包括这个\0,而不是实际的字符长度。
比如你的字符串实际字符数是19,加上\0后sizeof(ipstr)的值是20,这就导致循环多跑了一次。
问题2:循环越界并错误统计了\0
循环条件i < sizeof(ipstr)会让i从0跑到19(共20次):
- 当
i=19时,ipstr[i]就是末尾的\0,此时ipstr[i+1]属于越界访问内存(数组下标最大是19),程序行为是未定义的,但大概率ipstr[19] != ipstr[20],会进入else分支。 - 此时
num的值是0,所以ms['\0'] = 0 + 1 = 1,把空字符\0存入了map中。
而map<char, int>是按key的ASCII码升序排列的,\0的ASCII码是0,比所有字母都小,所以输出时会排在最前面。\0是不可见字符,所以输出时只显示它的计数1,后面跟着正常的2a3b...,就变成了你看到的12a3b3c4d3l4z。
额外提醒:map不适合统计连续字符
哦对了,顺便提一句:你的需求是统计连续字符的出现次数,但map会把所有相同字符的次数合并(比如如果字符串是aabbaa,map会把a的次数算成4,而不是2a2b2a)。如果你的需求确实是连续字符统计,应该直接输出结果而不是用map存储,这样更符合逻辑也更高效。
修复后的代码
基于map的修复版本(保持原存储逻辑)
把sizeof(ipstr)换成strlen(ipstr),同时调整循环条件避免越界(因为要比较ipstr[i]和ipstr[i+1],所以循环应该到i < strlen(ipstr) - 1):
#include <iostream> #include <cstring> // 需要包含strlen的头文件 #include <map> using namespace std; int main() { char ipstr[] = "aabbbcccddddzzzzlll"; cout << "size of string:" << strlen(ipstr) << endl; int num = 0; map<char, int> ms; // 循环到倒数第二个字符,避免i+1越界 for(int i = 0; i < strlen(ipstr) - 1; i++){ if(ipstr[i] == ipstr[i+1]) { num++; } else { ms[ipstr[i]] = num + 1; num = 0; } } // 处理最后一组连续字符 ms[ipstr[strlen(ipstr)-1]] = num + 1; for(auto it = ms.begin(); it != ms.end(); it++){ cout << it->second << it->first; } cout << endl; return 0; }
直接输出连续字符的版本(更贴合需求)
如果不需要存储统计结果,直接遍历输出连续字符的计数会更高效:
#include <iostream> #include <cstring> using namespace std; int main() { char ipstr[] = "aabbbcccddddzzzzlll"; int len = strlen(ipstr); if(len == 0) return 0; int count = 1; for(int i = 1; i < len; i++){ if(ipstr[i] == ipstr[i-1]){ count++; } else { cout << count << ipstr[i-1]; count = 1; } } // 输出最后一组连续字符 cout << count << ipstr[len-1] << endl; return 0; }
(注:原字符串里是4个z、3个l,map版本会按字母排序输出2a3b3c4d3l4z,直接输出版本会按原顺序输出2a3b3c4d4z3l,可根据需求选择)
内容的提问来源于stack exchange,提问作者sameer pradhan

