Блог пользователя nhan0123456

Автор nhan0123456, история, 7 недель назад, По-английски

Unexpected behavior when modifying a vector while calling push_back

I have a problem with the following Trie implementation.

Code 1 — RE

#include <bits/stdc++.h>
using namespace std;

struct Trie {
    struct Node {
        int child[2];
        long long sum, val;

        Node() {
            child[0] = child[1] = -1;
            sum = val = 0;
        }
    };

    vector<Node> vtNode;
    int root;

    Trie() {
        root = newNode();
    }

    int newNode() {
        int id = (int)vtNode.size();
        vtNode.push_back(Node());
        return id;
    }

    int getBit(long long x, int i) {
        return (x >> i) & 1;
    }

    void addNum(long long x) {
        int p = root;

        for (int i = 32; i >= 0; --i) {
            int c = getBit(x, i);

            if (vtNode[p].child[c] == -1) {
                cerr << vtNode[p].child[c] << "\n";

                vtNode[p].child[c] = newNode();

                cerr << vtNode[p].child[c] << "\n";
            }

            p = vtNode[p].child[c];
        }
    }
};

int main() {
    Trie trie;
    trie.addNum(12345);

    return 0;
}

Code 2 — works correctly

The only difference is that I store the result of newNode() in a local variable before modifying vtNode[p].child[c]:

void addNum(long long x) {
    int p = root;

    for (int i = 32; i >= 0; --i) {
        int c = getBit(x, i);

        if (vtNode[p].child[c] == -1) {
            cerr << vtNode[p].child[c] << "\n";

            int id = newNode();
            vtNode[p].child[c] = id;

            cerr << vtNode[p].child[c] << "\n";
        }

        p = vtNode[p].child[c];
    }
}

The second version works as expected, while the first version gives unexpected results.

For the first version, cerr prints -1 again after newNode():

-1
-1
-1
...

while the second version prints increasing positive indices.

My question

Why does this happen?

I initially thought these two pieces of code were equivalent:

vtNode[p].child[c] = newNode();

and

int id = newNode();
vtNode[p].child[c] = id;

Is this related to vector::push_back() causing reallocation and invalidating something?

I would like to understand what happens during the evaluation of vtNode[p].child[c] = newNode(); and why storing the return value in a local variable changes the behavior.

  • Проголосовать: нравится
  • +5
  • Проголосовать: не нравится

»
7 недель назад, скрыть # |
Rev. 3  
Проголосовать: нравится 0 Проголосовать: не нравится

I get the same output for both versions with alternating -1 and increasing indices in custom invocation with GNU G++17 (after changing cerr to cout). If you get different outputs, maybe there is undefined behavior somewhere else in the code. I doubt push_back() is the problem here. Which compiler are you using?

  • »
    »
    7 недель назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    I'm using Code::Blocks with GNU GCC Compiler. In the compiler settings, I have enabled: Have g++ follow the C++11 ISO C++ language standard [-std=c++11] So I'm compiling with GNU GCC / G++ using C++11. I tested the code in Code::Blocks, and I get different outputs between the two versions. Could this be related to the compiler version or the way Code::Blocks invokes GCC?

»
7 недель назад, скрыть # |
 
Проголосовать: нравится +2 Проголосовать: не нравится

I think Code 1 is undefined behavior in C++14 and before, but works in C++17 and later. See rule 19 of Order of Evaluation.