今天在写编译原理实验的时候掉入了一个很不起眼的深坑,坑到让我来专门写了一篇博客,记录这头(cao)秃(dan)的时刻。
起因
在做LR(0)求闭包的时候,使用迭代器遍历容器的同时,我又进行了对容器的操作,我以为这种自然而然的逻辑本不该引起这种错误,于是就开始了我漫长的差错之旅。如下是我最初的代码段:
`
vector<string> closure(vector<string> I)
{
vector<string>::iterator ite=I.begin();//闭包的迭代器
for(ite=I.begin();ite!=I.end();ite++)//遍历闭包元素
{
cout<<*ite<<endl;
for(int j=0;j<LR0.Vn.length();j++)//扫描所有在'·'后的非终结符,即对核求闭包
{
cout<<"error1:"<<I.end()-I.begin()<<endl;
int x1=(*ite).find('·',3);
int x2=(*ite).find(LR0.Vn[j],3);
cout<<x1<<","<<x2<<endl;//打印信息检错
if(x1==x2-1)//"·"占两个字符,但是只差一个不知道为什么。
{
for(int m=0;m<LRpro.size();m++)
{
if(LRpro[m].find(LR0.Vn[j])==0&&LRpro[m].find("·")==3)
{
I.push_back(LRpro[m]);
}
}
}
}
}
return I;
}`
寻找起因
首先用cout<<"error[i]"<<endl;
插入到程序的各个地方运行,找到不运行的点。每个地方插入的i不同,由此发现程序在那里崩掉了。当i=7时,我发现是在最外层,我认为最不会出问题的地方,遍历出错。当时我还固执地以为是其他原因,最终还是找到了循环体,当我不再ite++;
时,程序就不会再语法出错。最终我隐隐约约感觉是这样的:迭代器遍历容器时,容器的大小不可以发生变化。当容器扩大时,迭代器指向扩大的部分并不能得到容器元素,而是越界。
询问起因
较真的我,苦思冥想了很久,还是想不明白为什么会这样,巧在这时我的大神室友回来了,分析询问一波之后,大神的检测代码如下:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int cnt=2;
vector<int> pb;
pb.push_back(1);
vector<int>::iterator it;
for(it = pb.begin();it!=pb.end();++it){
if(cnt<=5)
pb.push_back(cnt);
cout<<*it<<endl;
}
return 0;
}
确确实实地发生了越界错误,最终在大神的帮助下总结该问题具体的描述并在网上找到了一篇解答的博客。
c++ STL中容器迭代器失效
确定起因
最终确定为更新时迭代器失效,于是我含泪写下实验总结:
这是我第一次在实验报告上无视格式,偏执地给字体变了个醒目的红色,同时给大神室友也上了一课。
解决方案
更新迭代器
对于迭代器失效,我首先想到的就是重新构造一个迭代器,即设置一个记录变量num,每次迭代器自加的时候num++,当我们进行了插入操作时,就break跳出循环然后重新构造一个迭代器,用num返回当时位置,继续操作。
考虑到此种做法过于低级,有失程序员的脸面,而且感觉养成这种习惯之后会在以后的程序编写中造成不良影响,并且降低可读性,我并没有去如此修正,而是尝试寻找一种更为体面的方法。
弃vector改list
此处参考:https://blog.csdn.net/hechao3225/article/details/55101344
由于vector和list的相似性,把代码中所有vector改成list甚至不用改任何其他操作,就完美高端地解决了问题,快乐。
vector,list,deque的相似和区别参考如下:
C++ list用法详解