返回信息流刷UVa OJ的时候遇到的超级诡异的问题,先上代码。(下面的是能AC的代码)
#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;
struct Node {
int left, right;
int value;
bool has_value;
Node() { left = right = value = 0; has_value = false; };
};
vector<Node> tree;
int root;
int createNode() {
tree.push_back(Node());
return (int)tree.size() - 1;
}
bool addNode(int value, string path) {
int node = root, t;
for (auto c : path) {
if (c == 'L') {
if (tree[node].left == 0) {
t = createNode();
tree[node].left = t;
//tree[node].left = createNode(); //把上面两行换成此行出错
}
node = tree[node].left;
}
else if (c == 'R') {
if (tree[node].right == 0) {
t = createNode();
tree[node].right = t;
//tree[node].right = createNode(); //把上面两行换成此行出错
}
node = tree[node].right;
}
}
if (tree[node].has_value) return false;
tree[node].value = value; tree[node].has_value = true;
return true;
}
int main() {
string s;
while (cin >> s) {
tree.clear();
root = createNode();
bool failed = false;
while (s != "()") {
int index_comma = s.find(',');
int value = stoi(s.substr(1, index_comma - 1));
string path = s.substr(index_comma + 1, s.size() - index_comma - 2);
if (addNode(value, path) == false) {
failed = true;
}
cin >> s;
}
queue<int> Q;
vector<int> ans;
Q.push(root);
while (!Q.empty()) {
int node = Q.front();
Q.pop();
if (!tree[node].has_value) {
failed = true;
break;
}
ans.push_back(tree[node].value);
if (tree[node].left > 0) Q.push(tree[node].left);
if (tree[node].right > 0) Q.push(tree[node].right);
}
if (failed) {
cout << "not complete" << endl;
}
else {
if (!ans.empty()) {
cout << ans[0];
for (int i = 1; i < ans.size(); i++) {
cout << ' ' << ans[i];
}
}
cout << endl;
}
}
return 0;
}
代码中27行和35行是原先写出来的,在vs2015上运行正常,结果提交到oj说Runtime Error。调试了半天也没发现问题,后来我一想调试好歹要用一样的编译器啊。然后打开clion换成gcc,果然运行时错误。
下面是oj上的gcc参数
C++11 5.3.0 - GNU C++ Compiler with options: -lm -lcrypt -O2 -std=c++11 -pipe -DONLINE_JUDGE
我用clion调试的时候发现运行时错误的原因在于代码中27行和35行调用createNode()时虽然能正常返回值,但它不会赋值给左边的tree[node].left,这简直太奇怪了。
我尝试着更换成先获取返回值再赋值,如上面代码所示,结果竟然可以了,一提交到oj上也AC了。
但就是非常不明白啊,难道这是gcc的bug?
求各位大神指点[ema13]
输入输出样例如下:
Sample Input
(11,LL) (7,LLL) (8,R)
(5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()
(3,L) (4,R) ()
Sample Output
5 4 8 11 13 4 7 2 1
not complete
原题如下:
好吧,这不是重点
这是一条镜像帖。来源:北邮人论坛 / cpp / #94398同步于 2017/1/23
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
我可能用了假的c++11
chenxiansf
2017/1/23镜像同步19 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
tree[node].left = createNode();
C++11没有规定先求赋值表达式左边还是右边的值(C++17先左边,后右边)。假如先求了左边的值。
左边是一个l-value,是tree这个vector里某个元素的存储空间。
但是,右边createNode()有副作用,它在push_back的时候,会造成vector大小变化。内部实现的方法,可能是把原来所有的存储空间拷贝到新分配的更大的数组中,然后把原来的存储空间释放。
也就是说,如果createNode()里面的push_back造成了vector大小变化,赋值运算符左边的l-value就指向一个已经被释放了的空间了。于是,有可能segmentation fault。
给跪了,暖神太6了,受我一拜
【 在 nuanyangyang 的大作中提到: 】
: tree[node].left = createNode();
: C++11没有规定先求赋值表达式左边还是右边的值(C++17先左边,后右边)。加入先求了左边的值。
: 左边是一个l-value,是tree这个vector里某个元素的存储空间。
: ...................
顺便秀一下比较摩登的语言:
Java:
int addElem(ArrayList<Integer> ar, int num) {
ar.add(num);
return ar.size();
}
void main() {
ArrayList<Integer> ar = new Arraylist<Integer>();
ar.set(0, 0);
ar.set(1, 1);
ar.set(2, 2);
for (int i = 0; i < 3; i++) {
ar.set(i, addElem(i));
}
}
ar.set(i, addElem(i));一定会先求出addElem(i)的值,然后调用ar.set方法。顺序已经保证了。
Scala:
val ar = ArrayBuffer(0,1,2);
for (i <- 0 until 3) {
ar(i) = addElem(i)
}
一样。Scala里,ar(i)=addElem(i)是语法糖,会展开成ar.update(i, addElem(i))
【 在 chenxiansf 的大作中提到: 】
: 刷UVa OJ的时候遇到的超级诡异的问题,先上代码。(下面的是能AC的代码)
Go 也(
【 在 nuanyangyang 的大作中提到: 】
: 顺便秀一下比较摩登的语言:
: Java:
: [code=java]
: ...................
顺便说一下,Rust也有和C++一样的问题。rust的复制表达式的求值顺序也没有规定。所以对Vec的push也会出现类似的重新分配问题。官方开发人员内部讨论帖: https://internals.rust-lang.org/t/rust-expression-order-of-evaluation/2605
我要立志学Scala了
【 在 nuanyangyang 的大作中提到: 】
: 顺便秀一下比较摩登的语言:
: Java:
: [code=java]
: ...................