为什么 string.insert(iterator,char) 连续工作六次而不是七次? (C++)
代码:
#include <iostream>
#include <string>
using namespace std;
string expand(string mask);
int main()
{
string tiny = "blah blah [a-e] blah blah";
string lengthy = "blah blah [a-i] blah blah";
cout << expand(tiny) << endl;
cout << expand(lengthy) << endl;
return 0;
}
string expand(string mask)
{
int i, range;
/* find the first bracket, grab start letter */
unsigned int bracket = mask.find("[");
char start = mask[bracket + 1];
/* point iterator at first bracket */
string::iterator here = mask.begin();
here += bracket;
/* find second bracket, calculate ascii range */
range = mask[bracket + 3] - mask[bracket + 1];
/* kill brackets and their contents*/
mask.erase(here, here + 5);
/*** This loop causes an error on the 7th iteration ****/
for(i = 0; i <= range; i++)
mask.insert(here, (start + range) - i);
return mask;
}
输出:
matt@Callandor:~/prog/tempVer$ g++ test.cpp -o 播放
matt@Callandor:~/prog/tempVer$ ./play
废话abcde废话
…等等等等defghi等等等等
* 检测到 glibc * ./play: free(): 下一个大小无效(快速): 0x08353068
======回溯:========= /lib/libc.so.6(+0x6c501)[0x5b5501]...
我遇到了一些尝试使用 string::insert(iterator,char); 时的奇怪行为我将它放在“for”循环中,我根本不移动迭代器,循环只是插入字符。如果我要插入六个或更少的字符,它会正常工作,但如果要插入七个或更多字符,它就会失败。
根据输出(见上文),看起来在六次插入之后,迭代器跳转到字符串的开头并开始插入垃圾。当程序完成时,我收到一个大而混乱的错误。
在尝试隔离原因时,我尝试了两个循环(没有一个循环触及迭代器):
for(i = 0; i < 6; i++)
mask.insert(here, (start + range) - i);
cout << mask << endl;
for(i = 0; i < 7; i++)
mask.insert(here, (start + range) - i);
cout << mask << endl;
第一个循环完成得很好,第二个循环导致了分段错误。
有人知道这是怎么回事吗?
Code:
#include <iostream>
#include <string>
using namespace std;
string expand(string mask);
int main()
{
string tiny = "blah blah [a-e] blah blah";
string lengthy = "blah blah [a-i] blah blah";
cout << expand(tiny) << endl;
cout << expand(lengthy) << endl;
return 0;
}
string expand(string mask)
{
int i, range;
/* find the first bracket, grab start letter */
unsigned int bracket = mask.find("[");
char start = mask[bracket + 1];
/* point iterator at first bracket */
string::iterator here = mask.begin();
here += bracket;
/* find second bracket, calculate ascii range */
range = mask[bracket + 3] - mask[bracket + 1];
/* kill brackets and their contents*/
mask.erase(here, here + 5);
/*** This loop causes an error on the 7th iteration ****/
for(i = 0; i <= range; i++)
mask.insert(here, (start + range) - i);
return mask;
}
Output:
matt@Callandor:~/prog/tempVer$ g++
test.cpp -o playmatt@Callandor:~/prog/tempVer$ ./play
blah blah abcde blah blah
���blah blah defghi blah blah
* glibc detected * ./play: free(): invalid next size (fast): 0x08353068
======= Backtrace: ========= /lib/libc.so.6(+0x6c501)[0x5b5501]...
I am running into some odd behavior when trying to use string::insert(iterator,char); I have it inside a 'for' loop where I don't move the iterator at all, the loop just inserts characters. It works fine if I have six or fewer characters to insert, but fails for seven or more.
Based on the output (see above) it looks like after six insertions the iterator jumps to the beginning of the string and begins inserting garbage. When the program finishes I get a large messy error.
While trying to isolate the cause I tried two loops (none of which touch the iterator):
for(i = 0; i < 6; i++)
mask.insert(here, (start + range) - i);
cout << mask << endl;
for(i = 0; i < 7; i++)
mask.insert(here, (start + range) - i);
cout << mask << endl;
The first completed just fine, the second caused a segmentation fault.
Anybody know what is going on here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在仔细检查您的代码后,我注意到您正在使用无效的迭代器。
简而言之,插入字符串会使其迭代器无效。插入后,
here
迭代器不再有效,因为除了其他实现特定细节之外,字符串容量可能会增加。当插入后再次使用here
迭代器而不首先将其重置为修改后的字符串中的有效位置时,这会导致未定义的行为。After pouring over your code, I noticed that you are using an invalidated iterator.
In a nutshell, insertion into a string invalidates its iterators. After your insert, the
here
iterator is no longer valid, because, among other implementation specific details, the string capacity could have increased. This leads to undefined behavior when thehere
iterator is used again after the insert, without first being reset to a valid spot in the modified string.可能是因为当字符串必须调整大小时,内部字符串将位于内存中的不同位置,并且您的迭代器(
此处
)变得无效。Probably because when the string has to resize, the internal string will be at a different location in memory and your iterator,
here
, becomes invalid.std::basic_string::insert
使所有迭代器无效。因此,here
在insert
调用后无效。因此,该程序具有未定义的行为,并且可以根据需要格式化您的硬盘:)说真的,您需要
here = mask.insert(here, (start + range) - i);
作为for 循环的主体。哦,您应该在继续之前确保
find
操作成功:)编辑:您最好将其重构为构建包含您想要的字符串的字符串添加,然后运行单个
插入
,而不是运行n
插入,因为n
插入结果可能具有二次时间的算法。std::basic_string<T>::insert
invalidates ALL iterators. Thereforehere
is not valid after theinsert
call. Therefore the program has undefined behavior and is allowed to format your hard disk if it want's to :)Seriously though, you want
here = mask.insert(here, (start + range) - i);
as the body of that for loop.Oh, and you should probably make sure the
find
operation succeeds before proceeding :)EDIT: You're probably better off refactoring this into something which builds a string containing what you want to add, then running a single
insert
, rather than runningn
inserts, becausen
inserts results in an algorithm with potentially quadratic time.我不太确定我是否有资格在这里给你建议,但它确实闻起来就像你正在索引超出数组的末尾。
I'm not quite sure I'm qualified to give you advice here, but it sure smells like you're indexing past the end of the array.
提问者问为什么它适用于 6 而不适用于 7。
其他发帖者通过回答第二部分已经很接近了,所以我将澄清插入可能使你的迭代器无效,但不一定。
如果 insert 必须重新分配内存,那么它将使迭代器无效。如果不需要重新分配内存,迭代器可能不会失效。
因此,前六次你很“幸运”,但最终还是被抓住了。
您还删除了 5 个元素,因此可能会使您的迭代器无效,但是在这种情况下,您的迭代器可能只是作为一个指针或指向其中一个的轻量包装器来实现,所以您再次“逃脱了它”。
通常,您不应该依赖于侥幸逃脱它,但如果您首先调用reserve(),然后只执行push_back()调用,则可以确定您的迭代器不会失效,直到您传递了保留的容量。
The questioner asked why it worked for 6 and not for 7.
The other posters have come close by answering the second part, so I will clarify that insert may invalidate your iterators, but will not necessarily.
If insert has to reallocate memory then it will invalidate iterators. If it does not have to reallocate memory the iterators might not become invalidated.
You were therefore "lucky" the first 6 times but finally got caught.
You are also erasing 5 elements so that might invalidate your iterators, however in this case your iterator is probably just implemented as a pointer or a light wrapper to one, so again you "got away with it".
You should not normally rely on getting away with it, but in the case you first call reserve() and then you just do push_back() calls, you can be certain your iterators will not be invalidated until you pass the capacity you have reserved.