这里有一个SSCCE(显示问题的简单示例):
#include <iostream>
#include <string>
#include <vector>
using namespace std;
enum Type {oInt, oFloat, oString, oArray};
struct Object; // fwd decl for union
union ObjectNative {
int oInt;
float oFloat;
const char* oString;
vector<Object>* oArray;
};
struct Object {
Type type;
ObjectNative obj;
};
vector<Object> exec(vector<string> tokens, vector<Object> stack);
void printStack(vector<Object> stack);
int main() {
vector<string> tokens{"test", "another test", "even more test", "test test", "test test test", "lotsa test"};
vector<Object> stack;
stack = exec(tokens, stack);
printStack(stack);
return 0;
}
vector<Object> exec(vector<string> tokens, vector<Object> stack) {
for (string s : tokens) {
ObjectNative nObj;
// !!!!!!!!!!!!!!!!!!!!!! THIS LINE !!!!!!!!!!!!!!!!!!!!!!
s[0];
nObj.oString = s.c_str();
Object obj = Object{oString, nObj};
stack.push_back(obj);
// debugging lines
printStack(stack);
cout << "-------" << endl;
}
return stack;
}
void printStack(vector<Object> stack) {
for (unsigned int i = 0; i < stack.size(); i ++) {
Object o = stack[i];
cout << o.obj.oString;
cout << endl;
}
}
注意我在exec
函数中标记的行:
s[0];
我只是访问字符串中的第一个字符;我甚至没有用它做任何事情!然而,如果我注释掉那一行,我会得到正确的输出:
test
-------
test
another test
-------
(etc....)
-------
test
another test
even more test
test test
test test test
lotsa test
但有了这句话,我…呃,我不确定发生了什么。这是输出:
test
-------
ä7R
another test
-------
ä7R
even more test
even more test
-------
test test
ä7R
ä7R
test test
-------
test test
test test test
test test test
test test
test test test
-------
lotsa test
ä7R
ä7R
lotsa test
ä7R
lotsa test
-------
lotsa test
ä7R
ä7R
lotsa test
ä7R
lotsa test
代码似乎没有什么问题,但很明显我做错了什么。为什么矢量会被破坏成这样,它究竟是如何被不处理字符串的第一个字符所引起的?
string::c_str()
返回的const char *
的生存期是有限的。如果您试图保留这些字符串,则需要将它们复制到专用的存储中。
字符串s
在每次迭代中被来自tokens
的新字符串替换。当for
循环在下一次迭代中将一个新字符串分配给s
时,可以使上一次迭代的c_str()
无效。
它起作用的事实可能是实现的一个幸运的侥幸:在引擎盖下,s
可能从tokens
中的条目中借用了字符串的存储空间。然而,对operator[]
的调用可能导致它生成自己的字符串私有副本。这可以解释c_str()
的行为差异。
要将副本保存在专用存储器中,您需要为其分配空间并将字符串复制到其中
nObj.oString = new char[s.size() + 1]; // allocate the space
std::strcpy( nObj.oString, s.c_str() ); // copy in the string
现在,从技术上讲,如果它包含ASCII NUL,它就不会复制到整个string
中。你最初的代码并不关心这些,我上面的建议并没有改变这一点。:-)您可以在<cstring>
中找到std::strcpy()
。
请注意,因为您已经为自己的存储分配了new[]
,所以在使用完delete[]
后,您必须记住它,否则会出现内存泄漏。请确保使用delete[]
,而不是delete
或free()
。
主要问题来自您的exec
:
for (string s : tokens)
{
ObjectNative nObj;
nObj.oString = s.c_str();
从语义上讲,tokens
向量是一个副本。此外,for (string s : tokens)
和s
也是循环中的临时副本。当循环退出时,s
将不再存在。所以当你做这样的事情时:
nObj.oString = s.c_str();
砰!您现在有一个悬空的const char *
。现在您的程序有未定义的行为,因为之后您尝试在main
中打印该堆栈。s[0];
导致任何行为差异的事实在这一点上都是无关紧要的。
我会做什么来解决这个问题,取决于你试图做什么,如下所示:
- 将原型更改为
void exec(const vector<string> &tokens, vector<Object> &stack);
- 将
s
修改为引用。例如CCD_ 31。只要tokens
的寿命不超过stack
,这应该是可以的