Як це може бути? Хіба пам'ять локальної змінної недоступна поза її функцією?
Ви орендуєте номер в готелі. Ви кладете книгу у верхню шухляду тумбочки і лягаєте спати. Ви перевіряєте наступного ранку, але "забуваєте" повернути свій ключ. Ви вкрали ключ!
Через тиждень ви повертаєтесь у готель, не заїжджаєте, забираєтесь у свою стару кімнату зі своїм викраденим ключем і зазираєте у шухляду. Ваша книга все ще є. Дивовижно!
Як це може бути? Чи не є вміст ящика готельного номера недоступним, якщо ви не взяли в оренду номер?
Ну, очевидно, що такий сценарій може трапитися в реальному світі без проблем. Немає таємничої сили, яка змушує вашу книгу зникнути, коли вам більше не дозволено перебувати в кімнаті. Також немає таємничої сили, яка заважає вам увійти до кімнати з викраденим ключем.
Для видалення вашої книги керівництву готелю не потрібно . Ви не укладали з ними контракту, в якому говорилося, що якщо ви залишите речі позаду, вони поріжуть це за вас. Якщо ви незаконно зайшли в свою кімнату з викраденим ключем, щоб повернути її назад, персонал служби готелю не зобов’язаний ловити вас, як ви прокрадаєтеся. Ви не уклали з ними контракт, який сказав "якщо я спробую прокрастися назад до свого кімната пізніше, ви зобов’язані мене зупинити ". Швидше за все, ви підписали з ними контракт, в якому сказано: "Я обіцяю, щоб пізніше не проникнути в свою кімнату", контракт, який ви розірвали .
У цій ситуації може статися все, що завгодно . Книга може бути там - вам пощастило. Чужа книга може бути там, і ваша може бути в печі готелю. Хтось міг бути там, коли ви заходите, розриваючи свою книгу на частини. Готель міг повністю зняти стіл і забронювати і замінив його гардеробом. Весь готель може бути ось-ось зірваний і замінений на футбольний стадіон, і ти загинеш у вибуху, поки ти крадешся навколо.
Ви не знаєте, що буде; коли ви виписалися з готелю і вкрали ключ незаконно використовувати пізніше, ви відмовилися від права жити в передбачуваному, безпечному світі , тому що ви вирішили порушити правила системи.
C ++ не є безпечною мовою . Це весело дозволить вам порушити правила системи. Якщо ви спробуєте зробити щось незаконне і нерозумне, як повернутися в кімнату, в якій ви не маєте права входити, і копати через стіл, який, можливо, вже не буде там, C ++ не зупинить вас. Більш безпечні мови, ніж C ++, вирішують цю проблему, обмежуючи владу - наприклад, набагато більш жорсткий контроль над клавішами.
ОНОВЛЕННЯ
Боже добро, ця відповідь привертає багато уваги. (Я не впевнений, чому - я вважав це просто "веселою" маленькою аналогією, але що завгодно.)
Я думав, що це може бути германним, щоб трохи оновити це ще кількома технічними думками.
Компілятори займаються створенням коду, який керує зберіганням даних, якими маніпулює ця програма. Існує маса різних способів генерування коду для управління пам’яттю, але з часом два основні методи стали закріпленими.
Перший - мати якусь "довгоживучу" зону зберігання, де "тривалість життя" кожного байта у сховищі - тобто період часу, коли він дійсно пов'язаний з якоюсь змінною програми - не може бути легко передбачити вперед часу. Компілятор генерує дзвінки в "менеджер купи", який знає, як динамічно розподілити сховище, коли це потрібно, і повернути його, коли воно більше не потрібно.
Другий спосіб полягає у тому, щоб мати «короткочасну» зону зберігання, де час життя кожного байту добре відомий. Тут життя йде за схемою "гніздування". Найдовші тривалість цих змінних буде розподілена перед будь-якими іншими короткочасними змінними і буде звільнена останньою. Короткоживучі змінні будуть виділятися після найдовших і будуть звільнені перед ними. Термін експлуатації цих короткоживучих змінних "вкладений" протягом життя довгоживучих.
Локальні змінні відповідають останній схемі; Коли метод введений, його локальні змінні оживають. Коли цей метод викликає інший метод, локальні змінні нового методу оживають. Вони будуть мертвими, перш ніж локальні змінні першого методу будуть мертвими. Відносний порядок початку та закінчення життя сховищ, пов'язаних з локальними змінними, може бути розроблений достроково.
З цієї причини локальні змінні зазвичай генеруються як зберігання в структурі даних "стека", оскільки стек має властивість, що перше, що натиснуто на нього, буде останньою справою.
Це все одно, що готель вирішує орендувати номери тільки послідовно, і ви не можете перевірити, поки всі з номером номеру вище, ніж ви звільнилися.
Тож давайте подумаємо про стек. У багатьох операційних системах ви отримуєте один стек на потік, і стек виділяється певним фіксованим розміром. Коли ви викликаєте метод, матеріал висувається на стек. Якщо потім передати покажчик на стек назад з вашого методу, як це робить оригінальний плакат, це лише вказівник на середину цілком допустимого блоку пам'яті мільйонів байтів. За нашою аналогією ви виходите з готелю; коли ви це робите, ви щойно виїхали із зайнятої кімнати з найбільшою кількістю номерів. Якщо ніхто за вами не заїжджає, а ви повернетесь до своєї кімнати незаконно, всі ваші речі гарантовано залишаться там у цьому конкретному готелі .
Ми використовуємо стеки для тимчасових магазинів, оскільки вони дійсно дешеві і прості. Для реалізації C ++ не потрібно використовувати стек для зберігання місцевих жителів; це може використовувати купу. Це не так, тому що це зробить програму повільніше.
Впровадження C ++ не потрібно, щоб сміття, яке ви залишили на стопі, залишати недоторканим, щоб ви могли пізніше повернутися за ним незаконно; цілком законно, щоб компілятор генерував код, який повертає до нуля все в "кімнаті", яку ви тільки що звільнили. Це не тому, що знову, це було б дорого.
Реалізація C ++ не потрібна для того, щоб, коли стек логічно скорочується, адреси, які раніше були дійсними, все ж відображалися в пам'яті. Реалізація дозволена сказати операційній системі "ми вже використовуємо цю сторінку стека. Поки я не скажу інше, видайте виняток, який знищує процес, якщо хтось торкнеться раніше дійсної сторінки стека". Знову ж таки, реалізація насправді цього не робить, оскільки це повільно і непотрібно.
Натомість реалізації дозволяють робити помилки та піти з цього. Більшість часу. Поки одного дня щось справді жахливе не піде не так, і процес вибухне.
Це проблематично. Правил дуже багато, і порушити їх випадково дуже просто. Я, звичайно, багато разів. І що ще гірше, проблема часто виникає лише тоді, коли після виявлення корупції пам'ять виявляється зіпсованою мільярдами наносекунд, коли дуже важко зрозуміти, хто її зіпсував.
Більш безпечні для пам'яті мови вирішують цю проблему, обмежуючи владу. У "звичайному" C # просто немає можливості взяти адресу локального і повернути його або зберегти для подальшого. Ви можете взяти адресу місцевого жителя, але мова вміло розроблена таким чином, що неможливо користуватися ним протягом життя місцевих кінцівок. Для того, щоб взяти адресу локального і передати його назад, вам потрібно перевести компілятор у спеціальний режим "небезпечний", а слово "небезпечно" поставити у вашій програмі, щоб звернути увагу на те, що ви, напевно, робите щось небезпечне, що могло б порушити правила.
Для подальшого читання:
address of local variable ‘a’ returned
; шоуInvalid write of size 4 [...] Address 0xbefd7114 is just below the stack ptr