[Powered by Google Translate] [Покажчики] [Rob Боуден] [Harvard University] [Це CS50] [CS50.TV] Давайте поговоримо про покажчики. До цих пір ми завжди просто називають речі в пам'яті явно по імені. Ми говорили Int N = 42, а потім, коли ми хочемо використовувати змінну п, ми просто називаємо його по імені ми даємо йому писати щось на зразок п * 2. Але це змінна повинна жити десь в пам'яті. Якщо ви хочете використовувати значення, яке в даний час зберігається в п, або змінити значення, що п тримає, Ваша програма повинна знати, де в пам'яті, щоб шукати с. Де в пам'яті змінних життя називають його адресу. Це як домашню адресу. Я можу знайти чийсь будинок тих пір, як я знаю їх домашню адресу, і комп'ютерна програма може знайти змінну, поки вона не знає його адресу в пам'яті. Що покажчики забезпечують спосіб безпосереднього вирішення цих адрес пам'яті. Багато влада в C приходить від того, щоб маніпулювати пам'яттю, як це. Але з великою силою приходить велика відповідальність. Покажчики можуть бути використані досить небезпечно, що багато мов програмування утаєння покажчиків повністю. Отже, чому C дають нам покажчики тоді? Як ви знаєте, аргументи функції C завжди копіюються в параметри функції. Таким чином, називаючи щось подібне підкачки на деякі змінні х і у не можу поміняти значення х і у в виконанні функції, хоча це може бути зручно. Як ми побачимо пізніше, переписування своп прийняти покажчиків на місцях необхідності буде замінений дозволяє йому впливати на своїх змінних абонента. Давайте розглянемо відносно простий приклад того, що може зробити покажчики. Скажімо, у нас є Int N = 4, і Int * pointer_to_n = & п. Вау! Трохи нового синтаксису, щоб покрити. По-перше, давайте інтерпретувати це і н. Пам'ятайте, що все, що в пам'яті є кілька адрес. Амперсанд називається "адресу" оператора. Таким чином, і п посилається на адресу в пам'яті, де зберігається н. Тепер, ми зберігаємо цю адресу в новій змінній, pointer_to_n. Який тип цієї нової змінної? Зірочка є частиною типу змінної, і ми прочитаємо типу Int *. Int * означає, що pointer_to_n це змінна, яка зберігає адресу цілого числа. Ми знаємо, що і п Int * Так як N є цілим числом, і ми беремо адресу с. Int * приклад тип покажчика. Як тільки ви починаєте бачити зірочки типу, Ви знаєте, що ви маєте справу з покажчиками. Так само, як ми можемо оголосити змінну як Int х і символ у, ми можемо сказати, Int * г і символ W *. Int * і символ * тільки нових типів для нас, щоб використовувати. Розташування * може піти куди завгодно перед ім'ям змінної. Таким чином, обидві Int * pointer_to_n - з * поряд з Int, як у нас тут - і Int * pointer_to_n з * поряд з pointer_to_n є дійсними. Але тут, я поставлю * поряд з Int. Неважливо, що ви віддаєте перевагу, просто будьте послідовні. Давайте намалюємо схему для цього. Ми спочатку повинні змінна п, яку ми будемо малювати, як маленька коробочка пам'яті. Для цього прикладу, давайте скажемо, що це поле знаходиться за адресою 100. Всередині цієї рамки, ми зберіганні значення 4. Тепер у нас є нова змінна, pointer_to_n. Вона має свою власну коробку в пам'яті, які ми будемо говорити знаходиться за адресою 200. Всередині цієї рамки, ми зберігаємо адреси п, які ми перед сказав, було 100. Часто в схемах, ви побачите це показано в буквальному стрілки залишивши pointer_to_n вікно вказуючи на вікно, яке зберігає н. Отже, що ми можемо реально зробити з pointer_to_n? Ну, якщо ми говоримо щось на кшталт * pointer_to_n = 8, це інша використання для зірочка , Що повністю відокремлений від використання зірочки в оголошенні змінної типу покажчика. Тут зірочка називається оператор разименованія. У нашій схемі, що pointer_to_n * = 8 означає, перейдіть у вікно, що містить pointer_to_n, слідуйте за стрілкою, , А потім призначити на поле в кінці стрілки значення 8. Це означає, що після цього рядка, якщо ми намагаємося використовувати п вона буде мати значення 8. 'Покажчик' Це слово використовується в багатьох різних контекстах. Тут ми намагаємося бути послідовними. Тип покажчика щось подібне Int *. У цьому відео, покажчик буде використовуватись тільки для позначення значення з типом покажчика, як pointer_to_n який має * Тип Int. Скрізь ми використовували, щоб просто сказати я, тепер ми можемо сказати, замість * pointer_to_n, і все буде працювати так само добре. Давайте розглянемо ще один простий приклад. Скажімо, у нас є Int N = 14; Int * покажчик = &n; п + +, і (* покажчик) + +. У першому рядку створюється нове вікно в пам'яті помічені н. На цей раз ми не будемо маркувати коробки з явним адресою, але він все ще є. Всередині коробки, ми зберіганні номер 14. У наступному рядку створюється другий прапорець покажчика. І всередині цієї рамки, ми зберігання покажчика на прапорець с. Отже, давайте малювати стрілку від покажчика до н. Тепер, N + + збільшує значення у вікні під назвою п, тому ми йдемо від 14 до 15. Нарешті, (* покажчик) + + йде до покажчика з написом, разименовивает значення в поле, що означає, слідуйте за стрілкою, де він вказує, і збільшує значення, що зберігається там, так що ми йдемо з 15 до 16. І це все. N тепер зберігає номер 16 після того, як збільшується в два рази - один раз безпосередньо за допомогою змінної п ім'я, а інший через pointer_to_n. Швидкий тест. Як ви думаєте, це означає, що якщо я намагаюся сказати щось подібне && п? Ну, давайте перепишемо це, як і (& N), яка робить те ж саме. (& N) повертає адресу змінної п в пам'яті. Але тоді то зовнішня амперсанд намагається повернути адресу адресу. Це все одно що намагатися робити і 2. Це не має сенсу, щоб отримати адресу тільки деяке число так як він не зберігається в пам'яті. Використання двох амперсанд в рядку ніколи не правильна ідея. Але тепер, що це означає, якщо я намагаюся сказати Int ** double_pointer = & покажчик? Тепер я створюю новий прапорець double_pointer, і всередині цього вікна я зберігаю адресу покажчика, що означає, я малюю стрілки з вікна double_pointer до покажчика вікна. Зверніть увагу на тип double_pointer, внутр. ** N було ціле, покажчик зберігається адреса п, і тому вона має * Тип Int. Тепер, double_pointer зберігає адресу покажчика, тому вона має тип Int. ** Отже, що ж ми думаємо, що це значить - ** Double_pointer = 23? Зверніть увагу, що я зараз разименованія в два рази. Просто дотримуйтесь вікно-і-стрілочної діаграми ми вже створили. По-перше, ми йдемо в поле з назвою double_pointer. Перший * означає, слідуйте за стрілкою раз. Зараз ми знаходимося на прапорець покажчика. Друга зірка говорить, слідуйте за стрілкою знову, і тепер ми перебуваємо в полі з назвою п, і ми встановлюємо значення цього ящика до 23. Зверніть увагу, що разименованія і "адреса" операторів є зворотними один від одного. Це дає мені можливість зробити щось подібне * & * & п = 42. Хоча це працює, ви ніколи не повинні робити щось подібне до цього на практиці. Що ми насправді тут робиш? Перший амперсанд захоплює адресу змінної с. Потім, у нас є разименовать оператор, який означає, що ми збираємося за цією адресою в пам'яті, таким чином, ми повернулися в с. Тепер візьміть адресу п раз і відразу разименованія, таким чином, ми повернулися на п і магазину 42. Таким чином, кожна пара * & просто скорочується. Існує спеціальний покажчик, званий нульовий покажчик. Це покажчик, що ми ніколи не повинні разименованія. Такий покажчик важлива, тому що вона дає нам можливість розрізняти покажчик, який повинен і не повинен бути разименован. Якщо ви спробуєте разименованія нульового покажчика, Зазвичай ваша програма дасть збій з помилкою сегментації, які ви могли бачити раніше. Так, скажімо, у нас є код Int * х = NULL; * х = 4. У цьому прикладі, це може здатися очевидним, що ми робимо щось погане, Але пам'ятайте, що нульовий дійсно може бути значення, повернене в результаті виклику функції як Танос, якщо Танос не може виділити пам'яті, запитаної користувачем. З цієї причини, якщо замість ми встановлюємо значення х в результаті виклику Танос, як в Int * х = Танос (SizeOf (INT)), то ми завжди повинні явно перевіряти щоб побачити, якщо нульове був повернутий. Якщо (х == NULL) / / UHOH! повернення, інакше ми можемо продовжити і сказати: * х = 4. Так що, знову ж таки, чому ми повинні постійно використовувати покажчики? Давайте подивимося на приклад програми, в якій ми повинні використовувати покажчики - Проста функція підкачки. Скажімо, у мене є два цілих числа, Int х = 4, і Int у = 15; і я хочу написати функцію, називається своп, який можна використовувати таким чином: своп (х, у). Після цієї лінії, значення всередині змінної х має бути 15, і значення в змінній у повинно бути 4. Значення всередині х і у були поміняні місцями. Без покажчиків, ми могли б спробувати щось подібне порожнечі свопу (INT A, INT B); Int TMP = B, B =, = TMP. Але, ви помітите проблеми з цим? Пам'ятайте, що значення зберігається в змінної це всього лише копія значення х, і значення в б копіюється з у. Будь-які зміни, внесені до А і В не будуть відображені в х і у. Таким чином, у той час як значення і б правильно помінялися місцями, х та у не змінилися взагалі. Тепер давайте змінимо своп функції, так що її аргументи є покажчиками на змінні воно повинно помінятися, ось так: порожнечу свопу (Int *, Int * б); Int TMP = * B; * B = *, * = TMP. Пам'ятайте, що свопи аргументи в даний час покажчиків, і тому ми повинні передати адресу х і у у виклику поміняти, от так: поміняти (і х, і у). Це тепер коректно свопи значення х і у. Давайте малювати блок-і-стрілки діаграми, щоб зрозуміти, чому це працює. Почнемо з нашими двома коробками в пам'яті, х і у. Усередині коробки для х це число 4, а всередині коробки для у нас є 15. Тепер, усередині виклику функції підкачки, у нас є ще дві коробки для аргументів а і б; вказує на вікно х і б вказує на поле у. Нова коробка створена для змінної TMP, і всередині нього ми зберігаємо результат разименованія б, , Що означає "слідувати стрілка від прапорець б. Таким чином, ми зберігаємо 15 всередині ТМП. Тоді, слідуючи стрілкою в б і зберігати тут результат разименованія, яка є значенням 4. Нарешті, ми слідуємо за стрілкою і зберігають те, що в даний час всередині TMP, що на 15. Зверніть увагу, що всі поля помічені х і у правильно поміняти місцями значення. Як тільки ми дізнаємося більше про Танос і динамічного управління пам'яттю, ми побачимо, що у нас немає вибору, окрім як використовувати покажчики. Проходячи через поле і стрілки діаграми для будь-якої програми може допомогти вам зрозуміти, що програма насправді. Мене звуть Боб Боуден, і це CS50. [CS50.TV] Це інше використання зірочки - bleah, я ненавиджу це слово. Скрізь ми використовували, щоб просто сказати п, ми можемо тепер сказати pointer_to_n - ні, ти не можеш - * pointer_to_n.