Які проблеми програмування найкраще вирішити за допомогою покажчиків? [зачинено]


58

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

Які хороші проекти чи проблеми вирішити, пов’язані з використанням покажчиків, щоб я міг їх краще зрозуміти?


Запитайте, які проблеми програмування найкраще вирішувати за допомогою покажчиків, а не які проекти / програми. Ви можете написати програму або за допомогою покажчиків, або ні - вони (проекти) просто занадто велика конструкція для змістовної відповіді.
Oded

37
Проблеми програмування створюються за допомогою покажчиків, не вирішених. :)
xpda

4
Власне, мені цікаво, які проблеми можна вирішити без покажчика. Дуже мало напевно.
deadalnix

4
@deadalnix, це залежить, якщо ви маєте на увазі явні вказівники чи неявні вказівники. Якщо ви маєте на увазі неявні вказівники (тобто є вказівники, які використовуються десь), то неможливо було б написати будь-яку програму, яка використовує стек або купу. Якщо ви маєте на увазі явні вказівники, то ви нестримні, ви можете робити будь-який розмір розподілу, створюючи нові кадри стека (в основному за допомогою виклику функції) та маніпуляції, використовуючи хвостові дзвінки, припускаючи, що вони оптимізовані компілятором.
dan_waterworth

3
Фу, деякі з цих відповідей жахливі.

Відповіді:


68

Маніпулювання великою кількістю даних у пам'яті - це місце, де вказівники дійсно світяться.

Передача великого об'єкта за посиланням рівносильна простому проходженню вздовж простого старого числа. Ви можете маніпулювати необхідними частинами безпосередньо на відміну від копіювання об'єкта, змінюючи його, а потім передавати назад копію, яку слід поставити на місце оригіналу.


12
Це чудова відповідь, і я хотів би додати, що для маніпулювання великою кількістю даних вам не потрібні великі об'єкти; періодично маніпулюючи великими предметами, це вдасться зробити, але ще більш поширеною проблемою є маніпулювання купою дрібних предметів дійсно дуже часто. Що, якщо ви задумаєтесь, - це майже всі комп'ютерні програми.
поштовх

44

Концепція вказівника дозволяє посилатися на дані за адресою, не дублюючи зберігання даних. Цей підхід дозволяє створити ефективні алгоритми, такі як:

  1. Сортування
    При переміщенні даних в алгоритмі сортування ви можете переміщувати покажчик замість самих даних - подумайте про сортування мільйонів рядків у рядку 100 символів; ви економите багато непотрібних рухів даних.

  2. Пов'язані списки
    Ви можете зберігати наступне та / або попереднє місце розташування елемента, а не всі дані, пов'язані із записом.

  3. Передача параметрів
    У цьому випадку ви передаєте адресу даних замість самих даних. Знову ж таки, придумайте алгоритм стиснення імен, який працює на мільйони рядків.

Концепція може бути розповсюджена на структури даних, такі як реляційні бази даних, де вказівник схожий на зовнішній ключ . Деякі мови не заохочують використання покажчиків, таких як C # та COBOL.

Приклади можна знайти в багатьох місцях, таких як:

Наступний пост може бути доречним певним чином:


14
Я б не сказав, що C # не заохочує використання покажчиків; натомість це не заохочує використання некерованих покажчиків - у C # є багато речей, які передаються посиланнями замість значення.
Blakomen

Гарне, хороше пояснення
Саббі,

5
Довелося підтримати коментар на C #. C # "посилання" є еквівалентними покажчикам, це просто те, що C # відволікає вас від того, що ви знаєте, що насправді ви просто маєте справу з вказівником на об'єкт в керованій купі.
MattDavey

1
@MattDavey: Посилання на C # не є покажчиками. Ви не можете додавати їх, використовувати їх для індексації масиву, використання декількох непрямих тощо. Наприклад, ви не можете мати посилання на посилання.
Біллі ONeal

5
@Billy ONeal думка про те, що "це лише вказівник, якщо ти можеш робити на ньому арифметику", відверто смішна, але я не маю терпіння сперечатися з тобою.
MattDavey

29

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

Пов'язані списки (поодиноко, подвійно і круговими зв’язками), дерева (червоно-чорний, AVL, трие, бінарний, розділення простору…) та графіки - це все приклади структур, які можна побудувати найбільш природно з посилань, а не просто на значення .


Ви забуваєте список, пов’язаний з XOR [=
dan_waterworth,

@dan_waterworth: Ні. Я занадто добре знаю, що чорна магія.
Джон Перді

8

Один простий спосіб - у поліморфізмі. Поліморфізм працює лише з покажчиками.

Крім того, ви використовуєте вказівники будь-коли, коли вам потрібно динамічне розподілення пам'яті. У 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 ++ ви використовуєте нову, щоб розподілити пам'ять для об'єкта та зберігати його в покажчику. Ви робите це в будь-який час, коли вам потрібно створити об'єкт під час виконання замість часу компіляції (тобто метод створює новий об'єкт і зберігає його у списку).

Щоб краще зрозуміти покажчики:

  1. Знайдіть проекти, які розігруються з традиційними c-рядками та масивами.
  2. Знайдіть деякі проекти, які використовують успадкування.

  3. Ось проект, на який я вирізав зуби:

Прочитайте у файлі дві nxn матриці та виконайте основні операції з векторним простором над ними та надрукуйте їх результат на екрані.

Для цього вам потрібно використовувати динамічні масиви та посилатися на свої масиви за вказівниками, оскільки у вас буде два масиви масивів (багатовимірні динамічні масиви). Після завершення проекту ви отримаєте досить гарне уявлення про те, як використовувати вказівники.


2
"Поліморфізм працює лише з покажчиками." Не точно: поліморфізм також працює з посиланнями. Нарешті, це вказівки, але я вважаю, що відмінність важлива, особливо для новачків.
Лоран Пірейн

ваш приклад для копіювання масиву в один рядок - це дійсно для копіювання нульового завершеного рядка в один рядок, а не будь-якого загального масиву.
tcrosley

8

Щоб справді зрозуміти, чому важливі покажчики, вам потрібно зрозуміти різницю між розподілом купи та розподілом стеків.

Нижче наведено приклад розподілу стеків:

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 ++.


1
(+1) також відомі як "статичні розподілені змінні" та "динамічні розподілені змінні" ;-)
umlcat

4

Напишіть будь-який нетривіальний проект на C, і вам доведеться визначити, як / коли використовувати вказівники. У C ++ ви здебільшого будете використовувати об'єкти з підтримкою RAII, які керують внутрішніми вказівниками, але в C-сировинних вказівниках є набагато більшу роль. Що стосується того, який проект ви маєте робити, він може бути будь-яким нетривіальним:

  • Крихітний і простий веб-сервер
  • Інструменти командного рядка Unix (менше, кішка, сортування тощо)
  • Якийсь фактичний проект, який ви хочете зробити заради себе, а не лише для навчання

Я рекомендую останній.


Тож я просто берусь за проект, яким я хочу займатися (як 2D гра), і передбачається, що мені знадобиться і вивчити вказівники?
dysoco

3
На мій досвід, найкращим способом навчання є реальні проекти, які трохи не досягають Ваших навичок. Концепції можна дізнатися, читаючи та записуючи невеликі програми «іграшки». Однак, щоб дійсно дізнатися, як ви можете використовувати ці поняття для написання кращих програм, вам просто потрібно написати програми, що не є іграшками.
Віктор Даль

3

Практично всі проблеми програмування, які можна вирішити за допомогою покажчиків, можна вирішити за допомогою інших безпечніших типів посилань (не посилаючись на посилання C ++ , але загальна концепція CS щодо змінної стосується значення даних, що зберігаються в іншому місці).

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

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

Щодо того, навіщо вам потрібно непряме, список досить довгий, але по суті дві ключові ідеї:

  1. Копіювання значень великих об’єктів відбувається повільно, і змушує об'єкт зберігатися в оперативній пам’яті двічі (можливо, це дуже дорого), але копіювання за посиланням знаходиться майже миттєво, використовуючи лише кілька байт оперативної пам’яті (для адреси). Наприклад, скажімо, що у вас в пам'яті близько 1000 великих об'єктів (кожен - приблизно 1 Мб оперативної пам’яті), і ваш користувач повинен мати можливість вибрати поточний об’єкт (на який діятиме користувач). Наявність змінної, selected_objectяка є посиланням на один з об'єктів, набагато ефективніше, ніж копіювання значення поточного об'єкта в нову змінну.
  2. Має складні структури даних, які посилаються на інші об'єкти, такі як пов'язані списки або дерева, де кожен елемент структури даних посилається на інші елементи структури даних. Перевага від посилань на інші елементи структури даних означає, що вам не доведеться переміщати кожен елемент у списку навколо в пам’яті лише тому, що ви вставили новий елемент на середину списку (може мати постійні вставки часу).

2

Маніпуляція зображеннями на рівні пікселів майже завжди легша та швидша за допомогою покажчиків. Іноді це можливо лише за допомогою покажчиків.


1
Я не думаю, що твердження «іноді це можливо лише за допомогою покажчиків» є правдивим. Є мови, які не піддають покажчику розробнику, але їх можна використовувати для вирішення проблем в одному просторі.
Томас Оуенс

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

@Thomas, хоча ви маєте рацію, питання стосується c ++, що не піддає покажчику розробнику.
Джонатан Хенсон

@Jonathan Незважаючи на це, якщо ви можете вирішити проблему мовою, яка не виставляє покажчиків, то також можна вирішити цю проблему мовою, яка не виставляє покажчиків без використання покажчиків. Це робить перше речення правдивим, але друге речення хибним - немає (наскільки мені відомо) проблем, які неможливо вирішити без використання покажчиків.
Томас Оуенс

1
Навпаки, обробка зображення часто включає "арифметику координат" - скажімо, взяття синуса і косинуса кута і множення його на x- і y-координати. В інших випадках потрібна обмотка або поза межами реплікації пікселів. У цьому сенсі арифметика вказівника досить неадекватна.
rwong

1

Покажчики використовуються у багатьох мовах програмування під поверхнею, не перекладаючи на них користувача. C / C ++ просто надає вам доступ до них.

Коли їх використовувати: якомога частіше, оскільки копіювання даних неефективне. Коли їх не використовувати: Коли потрібно дві копії, які можна змінити окремо. (Що в основному закінчиться копіюванням вмісту object_1 в інше місце в пам'яті та поверненням вказівника - цього разу вказує на object_2)


1

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

Якщо ви хочете дізнатися, чому саме вказівники настільки життєво важливі, я б запропонував дізнатися, що таке пов'язаний список, і спробувати написати його, не використовуючи покажчики. Я не ставив перед вами неможливого завдання (Підказка: вказівники використовуються для посилання на місця в пам'яті, як ви посилаєтесь на масиви в масивах?).


+1 для згадування структур даних, використання в алгоритмах є природним наслідком.
Матьє М.

0

Як приклад із реального життя, створення книги з обмеженими замовленнями.

Наприклад, канал ITCH 4.1 має концепцію «замінити» замовлення, де ціни (і, отже, пріоритет) можуть змінюватися. Ви хочете мати можливість приймати замовлення та переміщувати їх в інше місце. Реалізація подвійної черги з покажчиками робить операцію дуже простою.


0

Переглядаючи різні сайти StackExchange, я розумію, що задати подібне питання досить не модно. Буду чесним загрожувати критикою та негативом. Це не мається на увазі троліти або спалахувати, я маю на увазі лише допомогти, давши чесну оцінку питання.

І ця оцінка така: Це дуже дивне питання, яке потрібно задати програмісту C. Насправді все це говорить: "Я не знаю С." Якщо я проаналізую трохи далі та цинічніше, є підтекст подальшого виконання цього "питання": "Чи є якийсь ярлик, який я можу скористатись, щоб швидко та раптом здобути знання досвідченого програміста на С, не приділяючи жодного часу для самостійного навчання? " Будь-яка "відповідь", яку хтось може дати, не є заміною для того, щоб піти і робити розуміння основної концепції та її використання.

Я відчуваю, що конструктивніше, як інакше, навчитися добре вивчати C, з перших рук, ніж ходити в Інтернеті і питати людей про це. Коли ви добре знаєте C, ви не будете турбуватися задавати такі питання, це буде як запитання "які проблеми найкраще вирішити за допомогою зубної щітки?"


0

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

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

Поки ви можете вимагати певної пам’яті під час виконання і розміщувати свої дані в щойно придбаній пам'яті, ви можете зробити наступне:

  1. Ви можете мати саморозширювані структури даних. Це структури, які можуть розширювати себе, вимагаючи додаткової пам’яті до тих пір, поки їх потужність закінчиться. Ключовою властивістю кожної саморозширюваної структури є те, що вона складається з невеликих блоків пам'яті (звані вузлами, елементами списку тощо), залежно від структури), і кожен блок містить посилання на інші блоки (блоки). Ці "пов'язані" структури будують більшість сучасних типів даних: графіки, дерева, списки тощо.

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


0

Смішно, я щойно відповів на запитання про 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. Удачі, що маєш справу з губами, які тебе змушують


0

Покажчики дуже корисні для роботи з пристроями, нанесеними на пам'ять. Ви можете визначити структуру, яка відображає (скажімо) регістр керування, а потім призначити його за адресою фактичного реєстру управління в пам'яті та безпосередньо маніпулювати ним. Ви також можете вказати безпосередньо на буфер передачі на картці чи мікросхемі, якщо MMU відобразив його в просторі системної пам'яті.


0

Я бачу вказівники як мій вказівний палець, ми робимо пару речей:

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

пробачте, будь ласка, цю погану відповідь


0

Оскільки ваше питання позначено тегом C ++, я відповім на ваше запитання для цієї мови.

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

1. Поліморфізм

Вказівник базового класу дозволяє викликати віртуальний метод, який залежить від типу об'єкта, на який вказує вказівник.

2. Створення стійких об'єктів

Покажчики необхідні при динамічному створенні об'єкта (на купі замість стека). Це необхідно, якщо ви хочете, щоб термін експлуатації об’єктів був довшим, ніж обсяг, в якому вони створені.

Що стосується "хороших проектів чи проблем, які потрібно вирішити", як уже говорили інші, будь-який нетривіальний проект використовуватиме покажчики.


Можливо, хтось вважає за краще вирізати об'єкт, ніж використовувати вказівник. Щаслива налагодження: D
deadalnix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.