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.








I get the same output for both versions with alternating -1 and increasing indices in custom invocation with GNU G++17 (after changing
cerrtocout). If you get different outputs, maybe there is undefined behavior somewhere else in the code. I doubtpush_back()is the problem here. Which compiler are you using?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?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.
Thanks for pointing this out! I’ll look into the C++14/C++17 evaluation rules to understand exactly what is happening.
That seems to be the issue. Just to elaborate, in
vtNode[p].child[c] = newNode(), if the left side is sequenced first, thenvtNode[p].child[c]references some memory location before thepush_back, thenpush_backreallocates memory, and then=writes to the old memory location instead of the new one. So the problem was due to reallocation, indeed.I understand now. Thanks for the explanation.