Незалежно від того, розумієте ви це чи ні, переважна більшість програм, які ви використовували, так чи інакше використовують індекси. Можливо, ви коли-небудь відчували виняток NullPointerException. Як програміст, написаний вами код, швидше за все, буде використовувати покажчики, навіть якщо ви самі їх не реалізували.
Сьогодні я покажу вам, як працюють покажчики, тому ви можете перевірити, як працюють масиви і списки. працюють про програмування. Ця стаття буде більш теоретичною, ніж зазвичай, але дотримуйтеся її, покажчики дуже складні!
Код компіляції
Перш ніж копатися в індексах, ви повинні зрозуміти, як код створюється і виконується - можливо, ви вже знаєте це. У цьому розділі будуть досить загальні твердження - речі, які застосовні до більшості мов, але не обов'язково до всіх.
Давайте повернемося до початку. Кожен комп'ютер використовує двійковий код, ряд одиниць і нулів, що становлять сучасні технології, якими ми їх знаємо. Кодувати щось у двійковому форматі надзвичайно складно (файли можуть бути дуже заплутаними), оскільки це необроблені інструкції, необхідні центральному процесору або ЦБ для роботи. Це відомо як машинний код.
Наступним кроком від машинного коду є збірка. Це дещо зручний формат. Хоча все ще складно програмувати, це можливо. Збірка складається з серії простих команд для виконання завдань і відома як мова програмування низького рівня. Можна писати складні програми, але важко висловити абстрактні поняття і вимагає багато уваги.
Багато відеоігр і високопродуктивних програм мають деяку логіку, написану на асемблері, оскільки деякі реальні збільшення швидкості можна знайти, якщо ви знаєте, що робите. Однак для переважної більшості програмних проектів вам взагалі не потрібно знати будь-яку збірку.
Отже, якщо машинний код занадто складний для написання, а збірка занадто складна для програмування, з чим ви пишете код? Ось де приходять мови високого рівня. Мови високого рівня полегшують написання програм. Ви можете програмувати щось, що нагадує вашу рідну мову, і це легко висловити складні алгоритми. Можливо, ви чули про багато мов високого рівня (і ви напевно використовували програму, написану на них):
- Бейсик
- C ++
- шепелявість
Ці мови дуже старі, і багато з них були розроблені на початку 1950-х! Майже кожна сучасна мова програмування є мовою високого рівня, включаючи PHP і Python. Кожен день винаходиться все більше мов (хоча зараз їх, ймовірно, достатньо), але як саме ваш код як і раніше працює належним чином, якщо комп'ютери вимагають машинного коду?
Тут починається компіляція. Компілятор - це програма, яка перетворює ваш високорівневий код на форму, яку можна виконати. Це може бути інша мова високого рівня, але зазвичай це асемблер. Деякі мови (наприклад, Python або Java) перетворюють ваш код на проміжний етап, званий байт-кодом. Це вимагає повторної компіляції пізніше, що зазвичай робиться на вимогу, наприклад, при запуску програми. Це відомо як своєчасна компіляція, і це досить популярно.
Керування пам'яттю
Тепер, коли ви знаєте, як працюють мови програмування, давайте розглянемо управління пам'яттю мовами високого рівня. У цих прикладах я буду використовувати псевдокод - код, написаний не якоюсь конкретною мовою, а використовуваний для відображення концепцій, а не точного синтаксису. Сьогодні це буде в основному нагадувати C++, так як це найкраща мова високого рівня (на мій погляд).
У цьому розділі буде корисно, якщо у вас є уявлення про те, як працює ОЗП
Більшість мов мають змінні - контейнери, в яких зберігаються деякі дані. Вам слід визначити тип даних. Деякі динамічно типізовані мови, такі як Python або PHP, обробляють це для вас, але вони все ще повинні це робити.
Скажімо, у вас є змінна:
int myNumber;
Цей код оголошує змінну myNumber і дає їй тип даних integer. Після компіляції комп'ютер інтерпретує цю команду як:
«Знайдіть порожню пам'ять і зарезервуйте місце, досить велике для зберігання цілого числа»
Після виконання цієї команди цей біт пам'яті не може використовуватися іншою програмою. Він ще не містить ніяких даних, але зарезервований для вашої змінної myNumber.
Тепер присвойте значення вашій змінній:
myNumber = 10;
Щоб виконати це завдання, ваш комп'ютер звертається до своєї зарезервованої області пам'яті і змінює будь-яке збережене там значення на це нове значення.
Тепер, це все добре, але як місця пам'яті стають незарезервованими? Якби програми зарезервували всю пам'ять, яка їм подобається, ОЗП негайно заповнилося б - це зробило б дуже повільну систему.
Щоб уникнути цієї потенційної проблеми, багато мов реалізують збирач сміття, що використовується для знищення змінних (і, отже, звільнення зарезервованих областей пам'яті), які вийшли з області видимості.
Вам може бути цікаво, що таке сфера і чому це так важливо. Область дії визначає межі та тривалість життя змінних або будь-якої пам'яті, яку використовує програма. Змінна «виходить з області видимості», коли до неї більше не може отримати доступ якийсь код (саме тоді вступає збирач сміття). Ось приклад:
function maths() { int firstNumber = 1; } int secondNumber = 2; print(firstNumber + secondNumber); // will not work
Цей приклад не скомпілюється. Змінна firstNumber знаходиться всередині математичної функції, так що це її область. До нього не можна отримати доступ ззовні функції, в якій він був оголошений. Це важлива концепція програмування, і розуміння її вкрай важливе для роботи з покажчиками.
Цей спосіб обробки пам'яті називається стеком. Так працює переважна більшість програм. Вам не потрібно розуміти покажчики, щоб використовувати його, і він досить добре структурований. Браком стеку є швидкість. Оскільки комп'ютеру доводиться виділяти пам'ять, відстежувати змінні і запускати збірку сміття, виникають невеликі накладні витрати. Це добре для невеликих програм, але як щодо високопродуктивних завдань або додатків з великими обсягами даних?
Введіть: покажчики.
індекси
На перший погляд, покажчики звучать просто. Вони посилаються (вказують) на місце в пам'яті. Це може не відрізнятися від «звичайних» змінних у стеці, але, повірте мені, є величезна різниця. Покажчики зберігаються в купі. Це протилежність стека - він менш організований, але набагато швидший.
Давайте подивимося, як змінні призначаються в стеці:
int numberOne = 1; int numberTwo = numberOne;
Це простий синтаксис; Змінна numberTwo містить номер один. Це значення копіюється під час присвоювання зі змінної numberOne.
Якщо ви хочете отримати адресу пам'яті змінної, а не її значення, ви повинні використовувати знак амперсанда (&). Це називається адресою оператора і є невід'ємною частиною вашого інструментарію індексу.
int numberOne = 1; int numberTwo = &numberOne;
Тепер змінна numberTwo вказує на область пам'яті, а не копіює номер один у свою власну нову область пам'яті. Якби ви вивели цю змінну, вона не була б номером один (навіть якщо вона зберігається в комірці пам'яті). Це вивело б його область пам'яті (ймовірно щось як 2167, хоча це змінюється залежно від системи і доступної оперативної пам'яті). Щоб отримати доступ до значення, що зберігається в індексі, замість місця в пам'яті, ви повинні розіменувати індекс. Це прямо звертається до значення, яке в даному випадку буде номером один. Ось як ви розіменовуєте покажчик:
int numberTwo = *numberOne;
Оператор розіменування є зірочкою (*).
Це може бути важкою для розуміння концепції, тому давайте розглянемо її ще раз:
- Адреса оператора зберігає адресу пам "яті.
- Оператор розіменування (*) звертається до значення.
Синтаксис трохи змінюється при оголошенні індексів:
int * myPointer;
Тип даних int тут відноситься до типу даних, на який вказує індекс, а не до типу самого індексу.
Тепер, коли ви знаєте, що таке покажчики, ви можете робити з ними дійсно акуратні трюки! Якщо буде використано пам "ять, ваша операційна система запускається послідовно. Ви можете думати про RAM як про голубині діри. Багато отворів для зберігання чогось, тільки одна може бути використана одночасно. Різниця тут у тому, що ці отвори для голубів пронумеровані. Після вибору пам "яті ваша операційна система запускається з найменшого числа і працює. Він ніколи не буде стрибати між випадковими числами.
Під час роботи з індексами, якщо ви присвоїли масив, ви можете легко перейти до наступного елемента, просто збільшивши індекс.
Ось де це стає цікавим. Коли ви передаєте значення у функцію (використовуючи змінні, що зберігаються в стеку), ці значення копіюються до вашої функції. Якщо це великі змінні, ваша програма тепер зберігає їх двічі. Якщо ваша функція завершена, вам може знадобитися спосіб повернути ці значення. Як правило, функції можуть повертати тільки одну річ - що, якщо ви хочете повернути дві, три або чотири речі?
Якщо ви передаєте курсор на вашу функцію, копіюється тільки адреса пам'яті (яка крихітна). Це економить ваш процесор багато роботи! Можливо, ваш індекс вказує на величезний масив зображень - ваша функція може не тільки працювати з точно такими ж даними, які зберігаються в одній і тій же комірці пам'яті, але, як тільки це буде зроблено, вам не потрібно нічого повертати. Доглянута!
Ви повинні бути дуже обережні, хоча. Покажчики все ще можуть виходити за рамки видимості і збиратися складальником сміття. Однак значення, що зберігаються в пам'яті, не збираються. Це називається витоком пам'яті. Ви більше не можете отримати доступ до даних (оскільки покажчики були знищені), але вони все ще використовують пам'ять. Це загальна причина збою багатьох програм, і вона може несподівано провалитися, якщо є великий обсяг даних. У більшості випадків ваша операційна система вбиває вашу програму, якщо у вас більший витік (використання більшого обсягу ОЗП, ніж у системи), але це небажано.
Покажчики зневадження можуть бути кошмаром, особливо якщо ви працюєте з великими обсягами даних або працюєте в циклах. Їх недоліки і складність для розуміння дійсно варті того компромісу, який ви отримуєте від продуктивності. Хоча пам'ятаєте, вони не завжди можуть бути необхідні.
Ось і все на сьогодні. Я сподіваюся, що ви дізналися щось корисне про складну тему. Звичайно, ми не охопили все, що потрібно знати - це дуже складна тема. Якщо ви зацікавлені в отриманні додаткової інформації, я наполегливо рекомендую C++ протягом 24 годин.
Якщо це було трохи складно, подивіться на наше керівництво з найпростіших мов програмування. найпростіших мов найпростіших мов
Ви дізналися, як працюють покажчики сьогодні? Чи є у вас якісь поради і прийоми, якими ви хочете поділитися з іншими програмістами? Перейти в коментарі і поділитися своїми думками нижче!
