V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Caturra
V2EX  ›  C++

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

  •  
  •   Caturra ·
    caturra000 · 2020-01-06 21:02:50 +08:00 · 1940 次点击
    这是一个创建于 1802 天前的主题,其中的信息可能已经有所发展或是发生改变。
    #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 友们这个具体出现的问题是哪里,要怎样才能规避这种现象

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

    @wevsty 没想到这在 C++17 才有明确的规定,算是学习了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2765 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 13:29 · PVG 21:29 · LAX 05:29 · JFK 08:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.