Чому не вектор :: clear видалити елементи з вектора?

Коли я використовую clear() на std :: vector , він повинен знищити всі елементи у векторі , але замість нього т.

Код вибірки:

vector temp1(4);
cout << temp1.size() << std::endl;
temp1.clear();
cout << temp1.size() << std::endl;

temp1[2] = 343.5;//I should get segmentation fault here ....

cout << "Printing..... " << temp1[2] << endl;
cout << temp1.size() << std::endl;

Тепер я повинен отримати сегментацію під час спроби доступу до очищеного вектора, але замість цього він заповнює там значення (що, на мій погляд, дуже баггі)

Результат виглядає наступним чином:

4
0
Printing..... 343.5
0

Це нормально? Це дуже важка помилка, яка в основному загинула за моїм кодом протягом декількох місяців.

30
Несправність сегментації виробляється блоком керування пам'яттю, апаратним компонентом, який C ++ не вимагає. Якщо не вдалося отримати segfault викликало неправильну роботу вашої програми, то у вас є більш серйозні проблеми.
додано Автор Potatoswatter, джерело
Можливо, ви спробуєте проаналізувати свій код за допомогою статичного аналізатора: clang-analyzer.llvm.org . Я думаю, це буде означати цю помилку.
додано Автор Hack Saw, джерело
@KarolyHorvath: бібліотеки MSVC перевіряються за наборами налагоджувальних елементів за замовчуванням і не позначаються в збірках релізів. Це приголомшливо.
додано Автор Mooing Duck, джерело
якщо ви хочете зловити подібні помилки, скористайтеся зареєстрованим контейнером (gcc може це зробити, бібліотека зовнішньої stl тощо).
додано Автор Karoly Horvath, джерело
ви можете скористатись оператором at , який зробить перевірку кордонів і надасть виняток. Я рекомендую використовувати у замість []
додано Автор Bill, джерело
Інші інструменти, які ви можете використовувати, - Cleanify, Valgrind та ін. -, які позначатимуть такі помилки доступу до пам'яті, навіть якщо вони не призведуть до збою.
додано Автор layman, джерело

10 Відповіді

Ви не маєте права на випадок сегментації. З цієї причини несправність сегментації не є частиною C + +. Ваша програма вилучає всі елементи з вектора, і ви незаконно звертаєтесь до контейнера поза межами. Це невизначена поведінка, що означає, що все може статися. І дійсно, щось сталося.

77
додано
Знову тема компіляторів С ++, що формат туру HDD на UB: D
додано Автор Vorac, джерело
Від стандартного C ++ до "класу", повного компіляторів новачків - "ВИ маєте право мовчати" ... :-)
додано Автор GuruM, джерело

Коли ви отримуєте доступ поза межами вектора, ви отримуєте Undefined Behavior. Це означає, що все може статися. Щось.

Таким чином, ви можете отримати старе значення, сміття або seg-fault. Ви нічого не можете залежати від цього.

Якщо ви хочете перевірити обмеження, скористайтеся функцією-членом at() , а не operator [] . Замість виклику "Невизначене поведінка" викличе виключення.

27
додано

Від cppreference:

  void clear ();
 
     

Видаляє всі елементи з контейнера. Недійсні будь-які посилання, покажчики або ітератори, що стосуються елементів, що містяться. Може недійсним будь-які пробні ітератори. Багато реалізацій не виділяють виділену пам'ять після виклику clear() , фактично залишаючи потенціал вектора незмінним.

Тому причина відсутності очевидної проблеми полягає в тому, що вектор все ще має пам'ять, доступну в магазині. Звичайно, це лише специфіка виконання, але не помилка. Крім того, як зазначають інші відповіді, у вашій програмі також є невизначена поведінка для доступу до очищеного вмісту, тому технічно все може статися.

26
додано
@NateKohl Чому в редакції все-таки говориться: "Будь-які ітератори, що проходять повний цикл, не визнаються недійсними", Стандарт говорить, що вони можуть бути недійсними.
додано Автор 0x499602D2, джерело
@NateKohl І я оновив мою відповідь на ваш редагування.
додано Автор 0x499602D2, джерело
@Praetorian Я прочитав це, і мені стало незрозуміло, як cppreference скаже точно протилежне. Я видам свою відповідь, якщо це буде потрібно.
додано Автор 0x499602D2, джерело
@MarkRansom Стандарт просто говорить, що він очистить контейнер від усіх його елементів. Я не можу знайти нічого (поки що), що гарантує цю поведінку.
додано Автор 0x499602D2, джерело
@ 0x499602D2 Вимоги до контейнера послідовності (§23.2.3/Таблиця 100) містять a.clear() - знищує всі елементи у a. Ігнорує всі посилання, покажчики та ітератори, що відносяться до елементів a, і може призвести до втрати ітератора. cppreference неправильно щодо незмінності як capacity() , так і минулого -інтерналі залишаються дійсними.
додано Автор Praetorian, джерело
@ sasha.sochka Чому це важливо?
додано Автор Praetorian, джерело
Усі ітератори, покажчики та посилання, пов'язані з цим контейнером, є недійсними.
додано Автор 4pie0, джерело
Чи виділена частина гарантована стандартом, чи це швидше типова поведінка?
додано Автор Mark Ransom, джерело
@ 0x499602D2, я запитую лише тому, що cppreference.com це вікі, і він може містити неточну інформацію.
додано Автор Mark Ransom, джерело
Так, я думаю, що це вводить в оману. Див. Розділ stackoverflow.com/questions/ 6882799/& hellip; - я оновив cppreference, щоб відобразити, що це конкретно для конкретних рішень.
додано Автор Nate Kohl, джерело
Ви справді правильно. Я оновлю цю сторінку знову.
додано Автор Nate Kohl, джерело
І що зберігається в цій пам'яті після очищення ()?
додано Автор sasha.sochka, джерело
Але недоступність доступу з обмеженнями за допомогою оператора [] все ще є невизначеною поведінкою, тому абонент не може залежати від отримання старого значення. Різні параметри компілятора можуть, наприклад, писати значення сторонніх елементів у старі елементи.
додано Автор zindorsky, джерело

Давайте уявити собі, що ви багаті (можливо, ви або ви не ... що б то не було)!

Оскільки ви багаті, ви купуєте ділянку землі на Муреа (Навітряні острови, Французька Полінезія). Ви дуже впевнені, що це гарне майно, тому ви можете побудувати віллу на цьому острові, і ви там живете. У вашої вілли є басейн, тенісний корт, великий гараж та ще більш приємні речі.

Через деякий час ви залиш Муреа, оскільки ви думаєте, що це дійсно нудно. Багато спортивних, але мало людей. Ви продаєте свою землю та віллу і вирішите переїхати куди-небудь ще.

Якщо ви повернетесь через деякий час, ви можете зіткнутися з безліччю різних речей, але ви не можете бути впевненими в тому, що навіть один з них.

  • Ваша вілла може піти, її замінить клубний готель.
  • Ваша вілла може залишитися там.
  • Острів може затопитись.
  • ...

Хто знає? Хоча вілла не може більше належати до вас, ви навіть можете втекти в басейн або знову пограти в теніс. Поруч з ним може бути інша вілла, де ви зможете плавати в ще більшій басейні, і ніхто вас не відволікає.

У вас немає гарантії того, що ви будете шукати, якщо ви повернетесь знову, і це збігається з вашим вектором, який містить три покажчика в реалізаціях, на які я звернувся: (Імена можуть бути різними, але функція в основному однакова.)

  • begin points to the start of the allocated memory location (i.e. X)
  • end which points to the end of the allocated memory +1 (i.e. begin+4)
  • last which points to the last element in the container +1 (i.e. begin+4)

За допомогою команди clear, контейнер може повністю знищити всі елементи та скинути значення last = begin; . Найчастіше функція size() буде return last-begin; , і тому ви побачите розмір контейнера 0. Тим не менш, begin все ще може бути дійсним, і все ще може бути виділено пам'ять ( end може все ще бути begin + 4 ). Ви можете навіть спостерігати значення, які ви встановили перед очищенням ().

std::vector a(4);
a[2] = 12;
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;
a.clear();
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;

Друк:

шапка 4, ptr - 00746570, val 2 - 12
  шапка 4, ptr - 00746570, val 2 - 12

Why don't you observe any errors? It is because std::vector::operator[] does not perform any out-of-boundary checks (in contrast to std::vector::at() which does). Since C++ doesn't contain "segfaults" your program seems to operate properly.

Примітка. На MSVC 2012 оператор [] виконує граничні перевірки, якщо вони компілюються в режимі налагодження.

Ласкаво просимо до землі невизначеної поведінки! Це може статися або не буває. Ви, напевно, навіть не можете доглядати за однією обставиною. Ви можете ризикувати і бути сміливими, щоб поглянути на це, але це, ймовірно, не є способом створення надійного коду.

5
додано
Люби аналогії.
додано Автор Patrick, джерело

Оператор [] ефективний, але поставляється за ціною: він не виконує граничну перевірку.

Є більш безпечний, але ефективний спосіб доступу до вектора, як ітератори тощо.

Якщо вам потрібен вектор для довільного доступу (тобто не завжди послідовний), будьте дуже обережні, як писати свої програми, або використовуйте менш ефективні at (), які в тих самих умовах будуть викинуті виняток

5
додано
@NateKohl Дуже важливі коментарі (та редагування) дійсно!
додано Автор Antonio, джерело
Можливо, точніше сказати, що багато реалізацій не вилучаються після чіткої перевірки - перегляньте коментарі на відповідь 0x499602D2.
додано Автор Nate Kohl, джерело

ви можете отримати сегмент помилки, але це не обов'язково, оскільки після виходу з діапазону елементів вектора з operator [] після виклику clear() це просто невизначена поведінка. З вашої публікації, як ви хочете спробувати, якщо елементи будуть знищені, щоб ви могли використовувати для цієї мети публічну функцію at :

Функція автоматично перевіряє, чи знаходиться n в межах   дійсні елементи у векторі, викидаючи виняток, окрім винятку, якщо це   не є (тобто, якщо n більше або дорівнює його розміру). Це в   контраст з оператором-членом [], який не перевіряє границі.

крім того, після clear() :

Усі ітератори, покажчики та посилання, пов'язані з цим контейнером, є   недійсний.

http://www.cplusplus.com/reference/vector/vector/at/

2
додано

Якщо ви використовуєте

temp1.at(2) = 343.5;

замість

temp1[2] = 343.5;

ви знайдете цю проблему. Рекомендується використовувати функцію at() , а оператор [] не перевіряє границю. Ви можете уникнути помилки, не знаючи про втілення вектора STL.

До речі, я запускаю свій код у моєму Ubuntu (12.04) , виявляється, як ви говорите. Проте в Win7 повідомляється, що "Assertion Failed".

Що ж, це нагадує мені про тип stringstream. Якщо визначити речення

stringstream str;
str << "3456";

Якщо REUSE str , мені сказали, що це так

str.str("");
str.clear();

замістьjust using the sentence

str.clear();

І я спробував resize (0) в Ubuntu , виявляється марним.

2
додано
Мені цікаво - чи є спосіб зробити GCC працювати як MSVC і автоматично виявляти такі проблеми? Я зазвичай програма на Windows, і я знаю, що це дуже корисно, але я також працюю під Linux, і я хочу використовувати той же механізм перевірки помилок.
додано Автор Andrei Bârsan, джерело

Так, це нормально. clear() не гарантує перерозподілу. Спробуйте скористатись resize() після clear() .

1
додано
resize також не гарантує перерозподілу, але гарантує, що елементи будуть скинуті до відомих значень.
додано Автор Mark Ransom, джерело

спробуйте отримати доступ до елементів sup, ніж 4 , які ви використовуєте для конструктора, можливо, ви отримаєте помилку сегментації Інша ідея від cplusplus.com:

Очистити вміст

Видаляє всі елементи з вектора (які знищуються), залишаючи контейнер розміром 0.

Перерозподіл гарантовано не відбудеться, і векторна ємність не може бути змінена через виклик цієї функції. Типовою альтернативою, яка зумовлює перерозподіл, є використання swap:

vector().swap(x); //clear x reallocating

1
додано

Одне важливе доповнення до відповідей поки що: якщо клас вектор показується, він забезпечує деструктор, він буде викликаний очищенням (і на resize (0) )).

Спробуйте це:

struct C
{
    char* data;
    C()           { data = strdup("hello"); }
    C(C const& c) { data = strdup(c.data); }
    ~C()          { delete data; data = 0; };
};
int main(int argc, char** argv)
{
    std::vector v;
    v.push_back(C());
    puts(v[0].data);
    v.clear();
    char* data = v[0].data;//likely to survive
    puts(data);            //likely to crash
    return 0;
}

Ця програма, швидше за все, збігається з сегментацією помилки - але (дуже імовірно) не в char * data = v [0]. Data; , але в рядку puts (data); (використовуйте відладчик для перегляду).

Типові векторні реалізації залишають пам'ять виділеною недоступною і залишають її так само, як і після виклику деструкторів (однак, немає гарантії - пам'ятайте, це невизначена поведінка!). Остання річ, що було зроблено, полягала в тому, щоб налаштувати дані C-примірника на nullptr, і хоча це не є дійсним за змістом C ++/vector, пам'ять все ще там, тому може отримати доступ до неї (незаконно) без розбиття сегментації. Це відбудеться, коли вказівник char * data </​​code> вирівняється в позицію, як нульовий ...

0
додано
IT KPI C/С++ новым годом
IT KPI C/С++ новым годом
747 учасників

Чат обсуждения С/С++. - Вопросы "напишите за меня лабу" - это оффтоп. - Оффтоп, флуд, оскорбления и вбросы здесь не приняты. - За нарушение - предупреждение или mute на неделю. - За спам и рекламу - ban. Все чаты IT KPI: https://t.me/itkpi/1147