C ++ понимание доступа к глобальным переменным после очистки не дает какой-то ошибки - PullRequest
0 голосов
/ 12 декабря 2018

У меня есть вопрос о том, как глобальные переменные работают в c ++. Я знаю, что глобальные переменные являются злом, поэтому не в этом вопрос.Я просто хочу глубже понять, что происходит в следующем примере.

#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;
}

Дает такой вывод:

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

Почему размер вектора в деструктореB еще 2?

Когда я получаю доступ к элементам вектора, я получаю случайную память, которую я понимаю, потому что вектор очищается до B. Но для меня размер вектора должен быть 0 илидаже лучше выкинуть какую нибудь ошибку при запросе размера.Даже при использовании функции at () она не выдает ошибку, потому что размер по-прежнему равен 2.

Я также знаю, что могу это исправить, переключив инициализацию b и вектора.Мой вопрос заключается в том, почему этот конкретный пример не генерирует какую-то ошибку, потому что, по моему мнению, так и должно быть.

Примечание: как и мой комментарий: почему это поведение подпадает под неопределенное поведение вместо чтения или записинедопустимое место в памяти, так как вектор не существует в этой точке?Я вроде думал, что это может / должно генерировать ошибку сегмента, и я не понимаю, почему это не

1 Ответ

0 голосов
/ 12 декабря 2018

Неопределенное поведение означает, что поведение вашей программы не определено стандартом C ++.Соответствующий компилятор C ++ может делать что угодно с программой, которая имеет, или будет , демонстрировать неопределенное поведение (да, UB может путешествовать во времени).

Ваша программа показывает неопределенноеповедение путем доступа к v_ как к объекту до его создания в B::B.Учитывая, что он делает это, ничего в отношении выполнения ваших программ не указано и не ограничено стандартом C ++.

В этом случае компилятор обрабатывает доступ к UB так, как если бы он обращался к пустому std::vector.Это допустимо, потому что что-либо действительно .Затем программа работает, как если бы вы не сделали UB (кроме вышеуказанного признака), что также является допустимым вариантом.

Если мы представляем удаление UB в ctor, во время уничтожения вашей программы сноваэкспонаты UB.На этот раз доступ к v_ как к объекту vector после его уничтожения.Опять же, при этом поведение вашей программы не определяется или не ограничивается стандартом C ++ до, в и после UB.

В этом случае она ведет себя так, как если бы у вас был вектор с двумя значениямизначения которого равны 0. Это соответствует, потому что что-либо соответствует .

Одна из многих возможностей состоит в том, что данные были переработаны в куче, но указатели оставлены висящими.Обрабатывая «сгнившие» данные вектора как указатели, они по-прежнему указывают 2 sizeof(int) друг от друга, а .size() читает это как 2. Однако данные, на которые указывают, были переработаны в куче, и там есть другие данные.

...