vector操作过程中出现的地址错误
2023-08-25 15:09:42
start writing:2023.8.25 14:15
事情的起因又是邪恶的 yqt ...
为什么每次跟他都会遇到奇怪的事情
注——这只是一个对在劳累却有趣的 OI 生涯中遇到的插曲的记录,不是专业人士,对内容的研究也只是浅尝辄止,学个经验看个乐子就好了。
结论
省流:如果你用一个指针指向 vector 中一个元素,然后再对 vector 进行一些操作,有可能导致指针指向错误地址,因为在对 vector 进行操作时,vector 中数据的地址可能会发生改变。
起因
yqt 让我用线段树打一个 sb 题,反正就是区间修改,单点查询,只不过值域范围变成了
经过
这个时候,我想到“是不是可以写一个动态数组维护呢?懒得算数组大小了。”,然后就魔改成了这样:
#include<bits/stdc++.h> using namespace std; #define mid (l+r>>1) inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())x=(x<<3)+(x<<1)+(c^48); return x*f; } struct tree{ int v,l=-1,r=-1; }; int n,m,tot=-1,rt=-1; vector<tree>tr; inline void pushdown(int root){ if(tr[root].l<=0)tr.push_back({0,-1,-1}),tr[root].l=++tot; if(tr[root].r<=0)tr.push_back({0,-1,-1}),tr[root].r=++tot; tr[tr[root].l].v+=tr[root].v; tr[tr[root].r].v+=tr[root].v; tr[root].v=0; return; } void update(int &root,int l,int r,int x,int y,int v){ if(root<0)tr.push_back({0,-1,-1}),root=++tot; if(x<=l&&r<=y)return tr[root].v+=v,void(); pushdown(root); if(mid>=x)update(tr[root].l,l,mid,x,y,v); if(mid<y)update(tr[root].r,mid+1,r,x,y,v); } int query(int &root,int l,int r,int x){ if(root<0)tr.push_back({0,-1,-1}),root=++tot; if(l==r)return tr[root].v; pushdown(root); if(mid>=x)return query(tr[root].l,l,mid,x); else return query(tr[root].r,mid+1,r,x); } int main(){ n=read(),m=read(); while(m--){ int opt=read(),l=read(); if(opt&1){ printf("%d\n",query(rt,1,n,l)); }else{ int r=read(); update(rt,1,n,l,r,1); } } }
乍眼一看确实一点毛病没有,仔细看也没有啥问题,因为我就是把数组换成 vector 了而已。然后就莫名其妙的样例都过不去到处 RE,我把 vector<tree>tr
改成 vector<tree>tr(100000)
才勉强能过样例,交上去还是 RE 一片。
为啥呢?
在这个相对复杂的代码里面调试了很久还是找不到什么问题,只是在调试的过程中经常会出现其他变量挺正常的,但是就 root
压根没有值的现象,长这个样:
然后我想,既然是vector 的问题,那是不是就不能边 push_back
边查询刚加入的值呢?做了个实验,发现可以非常正常的运行。既然这个代码太麻烦了,那就稍微简化一下吧?于是我写了以下代码(为了观赏,删去了一些无用代码):
#include<bits/stdc++.h> using namespace std; struct tree{int v,l,r;}; inline tree New(){ tree p; p.v=0,p.l=-1,p.r=-1; return p; } int tot=-1,rt=-1; vector<tree>f; void update(int &root){ if(root<0)root=++tot,f.push_back(New()); if(f.size()<=10)update(f[root].l); } int main(){ tot=-1,rt=-1; update(rt); for(int i=0;i<=10;i++){ printf("%d\n",f[i].l); } }
果不其然,就简单到这种程度了还是会 RE。
加入了几个监视变量,我把所有调试按钮打开,一个一个的看过去,只看见这个 root
非常不听话,一开始挺正常的赋值,进入第二三层就变成了一些很奇怪的值 503
,-17891602
,514
等,到第三层结束去第四层,直接跟之前一样压根就不显示值了。
这个时候 yqt 的一点点用处就出来了,他说肯定是你这个 &
取地址有问题啊,我刚想反驳他,突然想到,是唉,如果地址指向错了,那也不差不多就是 RE 了吗??
尾声
我立马搜索:
发现:
我的天哪,这人出现的问题居然跟我出奇的一致,那就相信他好了。
我这时又给监视加了一个 &f[0].l
看在修改过程中地址有没有改变,然后就出现了下面一幕:
然后就不出所料的 RE 了。
大概是 vector 为了新加内容,申请空间的时候,地址发生了一些不大但很关键的变化,反映到程序上就是直接访问到错误地址导致 Segmentation fault。仔细看不难发现我这里展示的 &f[0].l
是先变化了一点点,然后又变回去了,不管怎样,只要变了,出事就很正常了。
所以下次用 STL 还是不要这么想当然了吧~也给我提供了一个调试新思路。
end writing 2023.8.25 14:52
本文作者:NBest
本文链接:https://www.cnblogs.com/NBest/p/17686961.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧