Ну, я в основному розумію, як користуватися покажчиками, але не тим, як найкраще їх використовувати для кращого програмування.
Які хороші проекти чи проблеми вирішити, пов’язані з використанням покажчиків, щоб я міг їх краще зрозуміти?
Ну, я в основному розумію, як користуватися покажчиками, але не тим, як найкраще їх використовувати для кращого програмування.
Які хороші проекти чи проблеми вирішити, пов’язані з використанням покажчиків, щоб я міг їх краще зрозуміти?
Відповіді:
Маніпулювання великою кількістю даних у пам'яті - це місце, де вказівники дійсно світяться.
Передача великого об'єкта за посиланням рівносильна простому проходженню вздовж простого старого числа. Ви можете маніпулювати необхідними частинами безпосередньо на відміну від копіювання об'єкта, змінюючи його, а потім передавати назад копію, яку слід поставити на місце оригіналу.
Концепція вказівника дозволяє посилатися на дані за адресою, не дублюючи зберігання даних. Цей підхід дозволяє створити ефективні алгоритми, такі як:
Сортування
При переміщенні даних в алгоритмі сортування ви можете переміщувати покажчик замість самих даних - подумайте про сортування мільйонів рядків у рядку 100 символів; ви економите багато непотрібних рухів даних.
Пов'язані списки
Ви можете зберігати наступне та / або попереднє місце розташування елемента, а не всі дані, пов'язані із записом.
Передача параметрів
У цьому випадку ви передаєте адресу даних замість самих даних. Знову ж таки, придумайте алгоритм стиснення імен, який працює на мільйони рядків.
Концепція може бути розповсюджена на структури даних, такі як реляційні бази даних, де вказівник схожий на зовнішній ключ . Деякі мови не заохочують використання покажчиків, таких як C # та COBOL.
Приклади можна знайти в багатьох місцях, таких як:
Наступний пост може бути доречним певним чином:
Я здивований, що жодна інша відповідь не згадала про це: покажчики дозволяють створювати неспоріднені та нелінійні структури даних, де елемент може бути пов'язаний з кількома іншими складними способами.
Пов'язані списки (поодиноко, подвійно і круговими зв’язками), дерева (червоно-чорний, AVL, трие, бінарний, розділення простору…) та графіки - це все приклади структур, які можна побудувати найбільш природно з посилань, а не просто на значення .
Один простий спосіб - у поліморфізмі. Поліморфізм працює лише з покажчиками.
Крім того, ви використовуєте вказівники будь-коли, коли вам потрібно динамічне розподілення пам'яті. У C це зазвичай трапляється, коли вам потрібно зберігати дані в масив, але ви не знаєте розмір під час компіляції. Потім ви зателефонуєте malloc, щоб розподілити пам'ять та вказівник для доступу до нього. Також, знаєте ви це чи ні, коли ви використовуєте масив, ви використовуєте вказівники.
for(int i = 0; i < size; i++)
std::cout << array[i];
є еквівалентом
for(int i = 0; i < size; i++)
std::cout << *(array + i);
Ці знання дозволяють робити дійсно класні речі, такі як копіювання всього масиву в один рядок:
while( (*array1++ = *array2++) != '\0')
У c ++ ви використовуєте нову, щоб розподілити пам'ять для об'єкта та зберігати його в покажчику. Ви робите це в будь-який час, коли вам потрібно створити об'єкт під час виконання замість часу компіляції (тобто метод створює новий об'єкт і зберігає його у списку).
Щоб краще зрозуміти покажчики:
Знайдіть деякі проекти, які використовують успадкування.
Ось проект, на який я вирізав зуби:
Прочитайте у файлі дві nxn матриці та виконайте основні операції з векторним простором над ними та надрукуйте їх результат на екрані.
Для цього вам потрібно використовувати динамічні масиви та посилатися на свої масиви за вказівниками, оскільки у вас буде два масиви масивів (багатовимірні динамічні масиви). Після завершення проекту ви отримаєте досить гарне уявлення про те, як використовувати вказівники.
Щоб справді зрозуміти, чому важливі покажчики, вам потрібно зрозуміти різницю між розподілом купи та розподілом стеків.
Нижче наведено приклад розподілу стеків:
struct Foo {
int bar, baz
};
void foo(void) {
struct Foo f;
}
Об'єкти, виділені на стеку, існують лише протягом тривалості виконання поточної функції. Коли виклик foo
виходить за межі сфери дії, це робить і змінна f
.
Один випадок, коли це стає проблемою, це коли вам потрібно повернути щось функційне, ніж цілісний тип, з функції (наприклад, структуру Foo з прикладу вище).
Наприклад, наступна функція призведе до так званої "невизначеної поведінки".
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
struct Foo f;
return &f;
}
Якщо ви хочете повернути щось struct Foo *
із функції, що вам дійсно потрібно, це розподіл купи:
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
return malloc(sizeof(struct Foo));
}
Функція malloc
виділяє об'єкт на купі і повертає вказівник на цей об’єкт. Зауважимо, що термін "об'єкт" тут використовується вкрай, що означає "щось", а не об'єкт у сенсі об'єктно-орієнтованого програмування.
Термін експлуатації виділених купи об'єктів контролюється програмістом. Пам'ять для цього об’єкта буде зарезервована, поки програміст не звільнить його, тобто зателефонувавши free()
або поки програма не вийде.
Редагувати : я не помітив, що це питання позначено як питання C ++. Оператори C ++ new
і new[]
виконують ту саму функцію, що і malloc
. Оператори delete
і delete[]
є аналогом free
. Хоча new
і delete
повинні використовуватися виключно для виділення і звільнення C ++ об'єкти, використання malloc
і free
абсолютно законно в коді C ++.
Напишіть будь-який нетривіальний проект на C, і вам доведеться визначити, як / коли використовувати вказівники. У C ++ ви здебільшого будете використовувати об'єкти з підтримкою RAII, які керують внутрішніми вказівниками, але в C-сировинних вказівниках є набагато більшу роль. Що стосується того, який проект ви маєте робити, він може бути будь-яким нетривіальним:
Я рекомендую останній.
Практично всі проблеми програмування, які можна вирішити за допомогою покажчиків, можна вирішити за допомогою інших безпечніших типів посилань (не посилаючись на посилання C ++ , але загальна концепція CS щодо змінної стосується значення даних, що зберігаються в іншому місці).
Покажчики , будучи конкретною реалізацією посилань низького рівня, де ви можете безпосередньо маніпулювати адресами пам'яті, є дуже потужними, але можуть бути дещо небезпечними для використання (наприклад, вказівки на місця пам'яті поза програмою).
Вигода від прямого використання покажчиків полягає в тому, що вони будуть трохи швидшими, не потрібно робити жодних перевірок безпеки. Такі мови, як Java, які безпосередньо не реалізують вказівники у стилі С, зазнають незначного впливу, але зменшать багато типів складних ситуацій налагодження.
Щодо того, навіщо вам потрібно непряме, список досить довгий, але по суті дві ключові ідеї:
selected_object
яка є посиланням на один з об'єктів, набагато ефективніше, ніж копіювання значення поточного об'єкта в нову змінну.Маніпуляція зображеннями на рівні пікселів майже завжди легша та швидша за допомогою покажчиків. Іноді це можливо лише за допомогою покажчиків.
Покажчики використовуються у багатьох мовах програмування під поверхнею, не перекладаючи на них користувача. C / C ++ просто надає вам доступ до них.
Коли їх використовувати: якомога частіше, оскільки копіювання даних неефективне. Коли їх не використовувати: Коли потрібно дві копії, які можна змінити окремо. (Що в основному закінчиться копіюванням вмісту object_1 в інше місце в пам'яті та поверненням вказівника - цього разу вказує на object_2)
Покажчики є важливою частиною будь-якої реалізації структури даних в C, а структури даних є важливою частиною будь-якої нетривіальної програми.
Якщо ви хочете дізнатися, чому саме вказівники настільки життєво важливі, я б запропонував дізнатися, що таке пов'язаний список, і спробувати написати його, не використовуючи покажчики. Я не ставив перед вами неможливого завдання (Підказка: вказівники використовуються для посилання на місця в пам'яті, як ви посилаєтесь на масиви в масивах?).
Як приклад із реального життя, створення книги з обмеженими замовленнями.
Наприклад, канал ITCH 4.1 має концепцію «замінити» замовлення, де ціни (і, отже, пріоритет) можуть змінюватися. Ви хочете мати можливість приймати замовлення та переміщувати їх в інше місце. Реалізація подвійної черги з покажчиками робить операцію дуже простою.
Переглядаючи різні сайти StackExchange, я розумію, що задати подібне питання досить не модно. Буду чесним загрожувати критикою та негативом. Це не мається на увазі троліти або спалахувати, я маю на увазі лише допомогти, давши чесну оцінку питання.
І ця оцінка така: Це дуже дивне питання, яке потрібно задати програмісту C. Насправді все це говорить: "Я не знаю С." Якщо я проаналізую трохи далі та цинічніше, є підтекст подальшого виконання цього "питання": "Чи є якийсь ярлик, який я можу скористатись, щоб швидко та раптом здобути знання досвідченого програміста на С, не приділяючи жодного часу для самостійного навчання? " Будь-яка "відповідь", яку хтось може дати, не є заміною для того, щоб піти і робити розуміння основної концепції та її використання.
Я відчуваю, що конструктивніше, як інакше, навчитися добре вивчати C, з перших рук, ніж ходити в Інтернеті і питати людей про це. Коли ви добре знаєте C, ви не будете турбуватися задавати такі питання, це буде як запитання "які проблеми найкраще вирішити за допомогою зубної щітки?"
Незважаючи на те, що вказівники дійсно світяться під час роботи з великими об’єктами пам'яті, все ж є спосіб зробити те ж саме без них.
Покажчик абсолютно необхідний для так званого динамічного програмування , це коли ви не знаєте, скільки пам’яті вам знадобиться до виконання вашої програми. У динамічному програмуванні ви можете запитувати шматки пам’яті під час виконання та розміщувати власні дані в них - тому вам потрібні вказівник або посилання (різниця тут не важлива), щоб мати можливість працювати з цими фрагментами даних.
Поки ви можете вимагати певної пам’яті під час виконання і розміщувати свої дані в щойно придбаній пам'яті, ви можете зробити наступне:
Ви можете мати саморозширювані структури даних. Це структури, які можуть розширювати себе, вимагаючи додаткової пам’яті до тих пір, поки їх потужність закінчиться. Ключовою властивістю кожної саморозширюваної структури є те, що вона складається з невеликих блоків пам'яті (звані вузлами, елементами списку тощо), залежно від структури), і кожен блок містить посилання на інші блоки (блоки). Ці "пов'язані" структури будують більшість сучасних типів даних: графіки, дерева, списки тощо.
Можна програмувати за допомогою парадигми OOP (Об'єктно-орієнтоване програмування). Весь OOP заснований на використанні не прямих змінних, а посилань на екземпляри класу (називаються об'єктами) та маніпулювання ними. Жоден екземпляр не може існувати без покажчиків (навіть якщо можна використовувати статичні класи навіть без покажчиків, це скоріше виняток).
Смішно, я щойно відповів на запитання про C ++ і розповів про покажчики.
Коротка версія - вам НІКОЛИ не потрібні вказівники, якщо 1) бібліотека, яку ви використовуєте, змушує вас 2) Вам потрібна нульова посилання.
Якщо вам потрібен масив, список, рядок тощо, просто розмістіть його на стеці та використовуйте об’єкт stl. Повернення або передача stl-об'єктів швидке (факт, що не перевіряється), оскільки у них є внутрішній код, який копіює покажчик замість об'єкта і копіює дані лише у тому випадку, якщо ви записуєте на нього. Це звичайний C ++, навіть не новий C ++ 11, який полегшить роботу бібліотечних авторів.
Якщо ви користуєтесь вказівником, переконайтесь, що він виконаний в одному з цих двох умов. 1) Ви передаєте дані, які можуть бути зведені нанівець. Приклад - необов’язкове ім’я файлу. 2) Якщо ви хочете віддати право власності. Як і в тому випадку, якщо ви переходите або повертаєте вказівник, у вас немає жодної копії його, а також не використовується вказівник, який ви віддаєте
ptr=blah; func(ptr); //never use ptr again for here on out.
Але я не використовував вказівники чи розумні покажчики дуже довго, і я профілював свою програму. Він бігає дуже швидко.
ДОДАТКОВА ПРИМІТКА: Я помічаю, що я пишу свої власні структури, і я їх передаю. То як мені це зробити без використання покажчиків? Це не контейнер STL, тому пропуск повз посилання повільний. Я завжди завантажую свій список даних / деки / карти тощо. Я не пам'ятаю повернення будь-яких об'єктів, якщо це не був якийсь список / карта. Навіть струна. Я переглянув код для окремих об'єктів, і помічаю, що я роблю щось подібне, { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }
тому я майже повертаю об'єкти (декілька) або попередньо розміщую / передаю посилання на заповнення (одиночний).
Тепер, якщо ви писали, що у вас є власні декети, карти тощо, вам потрібно буде використовувати покажчики. Але не потрібно. Нехай STL і, можливо, підвищить стурбованість з цього приводу. Вам просто потрібно записати дані та рішення. Не контейнери для їх зберігання;)
Я сподіваюся, що ви ніколи не використовуєте покажчики: D. Удачі, що маєш справу з губами, які тебе змушують
Покажчики дуже корисні для роботи з пристроями, нанесеними на пам'ять. Ви можете визначити структуру, яка відображає (скажімо) регістр керування, а потім призначити його за адресою фактичного реєстру управління в пам'яті та безпосередньо маніпулювати ним. Ви також можете вказати безпосередньо на буфер передачі на картці чи мікросхемі, якщо MMU відобразив його в просторі системної пам'яті.
Я бачу вказівники як мій вказівний палець, ми робимо пару речей:
пробачте, будь ласка, цю погану відповідь
Оскільки ваше питання позначено тегом C ++, я відповім на ваше запитання для цієї мови.
У C ++ існує розмежування між вказівниками та посиланнями, тому існує два сценарії, коли вказівники (або розумні покажчики) необхідні для полегшення певної поведінки. Їх можна використовувати в інших обставинах, проте ви запитаєте, як "найкраще їх використовувати", а в інших випадках є кращі альтернативи.
Вказівник базового класу дозволяє викликати віртуальний метод, який залежить від типу об'єкта, на який вказує вказівник.
Покажчики необхідні при динамічному створенні об'єкта (на купі замість стека). Це необхідно, якщо ви хочете, щоб термін експлуатації об’єктів був довшим, ніж обсяг, в якому вони створені.
Що стосується "хороших проектів чи проблем, які потрібно вирішити", як уже говорили інші, будь-який нетривіальний проект використовуватиме покажчики.