实在不懂,求问这段代码是不是踩了未定义行为的坑

2020-01-06 21:02:50 +08:00
 Caturra
#include<iostream>
#include<vector>
using namespace std;

struct test {
    
    vector<int> vec;
    
    test() : vec(1) {}
    
    int new_node(int i) {
        int n = vec.size();
        vec.push_back(i);
        return n;
    }
    
    void func(int i) {
        vec[0] = new_node(i);
        cout << vec[0] << endl;
    }
};

int main() {
    test t;
    for(int i = 0; i < 5; i++) t.func(i);
    return 0;
} 

(原代码比较复杂,这里经过化简,就是vec[0]存当前的长度,每次funcvec后面加一个数)

学的工地 C++,感觉好像没什么特别的实现,但就是产生奇怪的问题

本地用比较旧的 G++会产生运行时错误

而找了其它的在线 IDE 下,C++11 会输出

0
0
3
3
5

而在 C++17 下是我所期望的

1
2
3
4
5

想问问 V 友们这个具体出现的问题是哪里,要怎样才能规避这种现象

1814 次点击
所在节点    C++
7 条回复
May725
2020-01-06 21:34:44 +08:00
你这贴出来的代码没问题
Caturra
2020-01-06 21:45:42 +08:00
@May725 我也觉得没问题,但不同编译器的表现说明我代码里有坑
lcdtyph
2020-01-06 21:50:54 +08:00
```
vec[0] = new_node(i);
```
这行,new_node(i)里面有对 vec 的 expand 操作,这在 c++17 之前有可能会导致 vec[0]这个左值失效。
c++17 规定了求值顺序
wevsty
2020-01-06 21:57:08 +08:00
试了一下
icc 16.0.3 结果是 12345
clang 9.0 结果是 12345
MSVC 19.23 结果是 12345
只有 gcc 给的结果是 00335
我觉得应该是你这个代码触发了 gcc 的什么奇怪的优化或者 bug,不是一个 ub。
lcdtyph
2020-01-06 22:00:45 +08:00
@wevsty
这就是 ub,不要随便怀疑编译器…
你可以把 vec 初始化时候的那个参数 1 改成 10,这样保证 5 次 push 都不会让内部的地址失效。
wevsty
2020-01-06 22:26:00 +08:00
@lcdtyph
仔细看了一下标准,确实 C++17 标准新增了一条。
每个简单赋值表达式 E1=E2 和每个复合赋值表达式 E1@=E2 中,E2 的每个值计算和副作用均按顺序早于 E1 的每个值计算和副作用。
这个是明确了需要先算 E2,之前的标准并没有这样的要求,没有要求那就是 UB 了。
Caturra
2020-01-06 23:05:51 +08:00
@lcdtyph 感谢,旧编译器主动扩容后虽然也有奇怪的行为,不过起码知道哪里出问题了

@wevsty 没想到这在 C++17 才有明确的规定,算是学习了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/635604

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX