2

I have a question about how global variables work in c++. I know global variables are evil so that is not the point of this question. I just want a deeper understanding of what is happening in the following example.

#include <cstdint>
#include <iostream>
#include <vector>

class B
{
public:
  B();
  ~B();
private:
  static std::vector<int32_t> v_;
};

B::B()
{
  std::cout << "v_.size() = " << v_.size() << std::endl;
  for (auto i : v_)
    std::cout << i << std::endl;
}

B::~B()
{
  std::cout << "v_.size() = " << v_.size() << std::endl;
  std::cout << "v_[0] = " << v_[0] << std::endl;
  std::cout << "v_.at(0) = " << v_.at(0) << std::endl;
  for (auto i : v_)
    std::cout << i << std::endl;
}

B b;

std::vector<int32_t> B::v_ { 5, -7 };

int main()
{
  return 0;
}

Gives this output:

$ ./test 
v_.size() = 0
v_.size() = 2
v_[0] = 0
v_.at(0) = 0
0
0

Why is the size of the vector in the destructor of B still 2?

When I access the elements of the vector I get random memory, which I sort of understand because the vector gets cleaned up before B. But to me the size of the vector should be 0 or even way better throw some sort of error when asking for the size. Even when using at() function It doesn't throw an error because the size is still 2.

I also know I can fix this by switching the initialization of b and the vector. My question is more why this specific example doesn't throw some sort of error, cause in my opinion it should.

Note: like my comment: why does this behavior fall under undefined behavior instead of reading or writing an illegal memory location since the vector doesn't exist at that point? I was kinda thinking this would/should generate a seg fault and I don't understand why it doesn't

rinn2883
  • 366
  • 1
  • 14
  • "_My question is more why this specific example doesn't throw some sort of error, cause in my opinion it should._" Why should it throw any error? Undefined behavior is undefined. – Algirdas Preidžius Dec 12 '18 at 14:30
  • Possible duplicate of [Destruction order of static objects in C++](https://stackoverflow.com/questions/469597/destruction-order-of-static-objects-in-c) – Matthieu Brucher Dec 12 '18 at 14:32
  • The vector doesn't exist at that point. There is nothing it *can* do. – molbdnilo Dec 12 '18 at 14:43
  • Your program exhibits undefined behavior in `B::B` already, by way of accessing `B::v_` before its lifetime has started. – Igor Tandetnik Dec 12 '18 at 14:54
  • mhh so this falls under undefined behavior instead of reading or writing an illegal memory location since the vector doesn't exist at that point? I was kinda thinking this would/should generate a seg fault and I don't understand why it doesn't – rinn2883 Dec 12 '18 at 18:38

1 Answers1

0

Undefined behavior means that the behavior of your program is not defined by the C++ standard. A conforming C++ compiler can do anything with a program that has, or will, exhibit undefined behavior (yes, UB can time travel).

Your program exhibits undefined behavior by accessing v_ as an object prior to it being constructed in B::B. Given that it does this, nothing about your programs execution is specified or constrained by the C++ standard.

In this case, the compiler treats the UB access as if it was accessing an empty std::vector. This is valid, because anything is valid. The program then proceeds as-if you hadn't done the UB (other than the above symptom), which is also a valid option.

If we imagine removing the UB in the ctor, during destruction your program again exhibits UB. This time by accessing v_ as a vector object after it was destroyed. Again, by doing this the behavior of your program is not defined or constrained by the C++ standard, before, at, and after the UB.

In this case, it behaves as if you have a vector of 2 values whose values are 0. That is conforming, because anything is conforming.

One of many possibilities is that the data was recycled on the heap, but the pointers where left dangling. Treating the vector's "rotted" data as pointers they still point 2 sizeof(int) apart, and .size() reads that as 2. The data pointed to, however, has been recycled on the heap, and there is different data there.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524