векторное назначение на неинициализированном фрагменте памяти и подобных проблемах - PullRequest
0 голосов
/ 02 марта 2010

vector<vector<string> > test например.
G ++, вы можете сделать

test.reserve(10);
test[0] = othervector;
test[9] = othervector;

Не падает. Теория говорит, что вы не должны делать это так, потому что вы назначаете вектор для фрагмента памяти, который считает , это вектор.
Но это работает так же, как следующий:

#include <string>
#include <vector>
#include <iostream>
using namespace std;
int main(){
    vector<string> first_vector;
    vector<string> &second_vector = *(vector<string>*)new char[sizeof(vector<string>)];
    first_vector.push_back("whatever");
    first_vector.push_back("whatever2");
    first_vector.push_back("whatever3");
    second_vector = first_vector;
    cout << "0 " << second_vector[0] << " \n";
    cout << "1 " << second_vector[1] << " \n";
    cout << "2 " << second_vector[2] << " \n";
}

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

Что ж, для меня это явно неопределенное поведение, единственная проблема в том, что он работает как положено, я нашел довольно много из них в кодовой базе, которую я сейчас проверяю.

Есть ли еще случаи, подобные этому, в остальных контейнерах? Никогда не видел такого, который кажется таким простым, чтобы совершить ошибку, но он действительно работает, даже если вы допустили ошибку.

РЕДАКТИРОВАТЬ : Речь идет не о том, как правильно выполнить вышеизложенное, или о жалобах на поведение компиляторов, о попытках найти похожие проблемы, которые легко могут возникнуть, и которые действительно трудно обнаружить позже, например, эту. *

Ответы [ 4 ]

2 голосов
/ 02 марта 2010

«Есть ли еще случаи, подобные этому, в остальных контейнерах?»

Тонны.И я не шучу.Я могу придумать буквально тысячи из них, по всему стандарту.

Например, почти все функции, которые принимают диапазон итераторов , могут сломаться неприятными способами, если вы передадите два несвязанных итератора,Они также могут «работать» тихо, но неправильно, особенно если вы передадите два векторных итератора.

Аналогично, во всех известных мне реализациях любой тип T reinterpret_cast<T&>(&random_bytes).operator=(T()); может работать для некоторых значений random_bytes.Это все еще UB.

2 голосов
/ 02 марта 2010

То есть вы просите о произвольном злоупотреблении STL?

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

Еще одна распространенная ошибка - не учитывать недействительность итератора (например, при вставке / удалении во время цикла).

При неправильном приведении можно, вероятно, создавать всевозможные сценарии.

0 голосов
/ 02 марта 2010

Вы должны использовать vector :: resize вместо vector :: reserve.

Я тоже однажды сделал эту ошибку, и она работала без проблем в 32-битной Windows, но вылетала, когда все было скомпилировано как 64-битная.

Вот что означает «неопределенный»: он может работать или может внезапно перестать работать. Нет гарантии (даже не гарантия, что она выйдет из строя).

0 голосов
/ 02 марта 2010

«Неопределенное поведение» - это просто: не определено. Вы обнаружили ситуацию на одном компиляторе под одной ОС, где поведение соответствует ожидаемому. Это на самом деле довольно часто - компиляторы не принимают «неопределенное поведение», чтобы означать «автоматически segfault»! Но проблема с неопределенным поведением состоит в том, что, поскольку Стандарт не гарантирует этого, если вы позже смените ОС, или компилятор, или обновите до более поздней версии того же компилятора, вы не можете полагаться на то, что поведение остается прежним. Это может хорошо взорваться на вашем лице.

...