Навіщо використовувати вказівники? [зачинено]


356

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

В основному у мене є три питання:

  1. Навіщо використовувати вказівники на звичайні змінні?
  2. Коли і де я повинен використовувати вказівники?
  3. Як ви використовуєте покажчики з масивами?

5
Раніше обговорювалося в цьому питанні Надія, яка допомагає!
Дуг Т.

4
Список книг см stackoverflow.com/questions/388242 / ... . Після Java я вважаю прискорений C ++ дуже корисним.
Джабба

163
Ми використовуємо вказівники, оскільки простіше надати комусь адресу до вашого будинку, ніж надати копію вашого будинку всім.
Rishi Dua

14
@RishiDua Це єдине найкраще пояснення вказівника, який я коли-небудь стикався. Дякую за це, це розширило моє розуміння :)
Джош

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

Відповіді:


172
  • Навіщо використовувати вказівники на звичайні змінні?

Коротка відповідь: Не варто. ;-) Покажчики повинні використовуватися там, де ви нічого не можете використовувати. Це тому, що відсутність відповідного функціоналу, відсутні типи даних або для чистої продуктивності. Детальніше нижче ...

  • Коли і де я повинен використовувати вказівники?

Коротка відповідь тут: Там, де ви нічого не можете використовувати. У C у вас немає підтримки для складних типів даних, таких як рядок. Також немає можливості передавати змінну "посиланням" на функцію. Ось де вам потрібно використовувати вказівники. Також ви можете мати їх, щоб вони вказували практично на що завгодно, пов'язані списки, члени структур і так далі. Але не будемо тут розбиратися в цьому.

  • Як ви використовуєте покажчики з масивами?

З невеликим зусиллям і багато плутанини. ;-) Якщо ми говоримо про прості типи даних, такі як int та char, між масивом та вказівником є ​​невелика різниця. Ці декларації дуже схожі (але не однакові - наприклад, sizeofповертають різні значення):

char* a = "Hello";
char a[] = "Hello";

Ви можете дістатись до будь-якого елемента в масиві, як цей

printf("Second char is: %c", a[1]);

Індекс 1, оскільки масив починається з елемента 0. :-)

Або ви могли однаково це зробити

printf("Second char is: %c", *(a+1));

Оператор вказівника (*) потрібен, оскільки ми кажемо printf, що хочемо надрукувати символ. Без * було б надруковано символьне представлення самої адреси пам'яті. Тепер ми використовуємо сам символ. Якби ми використовували% s замість% c, ми б попросили printf надрукувати вміст адреси пам'яті, на яку вказано "a" плюс один (у цьому прикладі вище), і нам не довелося б ставити * попереду:

printf("Second char is: %s", (a+1)); /* WRONG */

Але це не було б просто надруковано другого символу, а натомість усі символи в наступних адресах пам'яті, поки не знайдеться нульовий символ (\ 0). І ось тут починають ставати небезпечні речі. Що робити, якщо ви випадково спробуйте надрукувати змінну типу цілого числа замість символу char із форматером% s?

char* a = "Hello";
int b = 120;
printf("Second char is: %s", b);

Це дозволить надрукувати все, що знайдеться в адресі 120 пам'яті, і продовжуватиме друкувати, поки не буде знайдено нульовий символ. Неправильно та незаконно виконувати цю операцію printf, але це, ймовірно, все одно спрацює, оскільки вказівник насправді має тип int у багатьох середовищах. Уявіть проблеми, які можуть виникнути, якби замість цього використовувати sprintf () і призначити таким чином занадто довгий "масив char" іншій змінній, що виділила лише певне обмежене місце. Ви, швидше за все, закінчите записувати щось інше в пам’яті і призведе до краху вашої програми (якщо вам пощастить).

О, і якщо ви не призначите значення рядка для масиву / покажчика char, коли ви оголошуєте його, ОБОВ'ЯЗКОВО виділити йому достатній об'єм пам'яті, перш ніж надати йому значення. Використання malloc, calloc або подібного. Це оскільки ви оголосили лише один елемент у своєму масиві / одну єдину адресу пам'яті, на яку слід вказати. Ось ось кілька прикладів:

char* x;
/* Allocate 6 bytes of memory for me and point x to the first of them. */
x = (char*) malloc(6);
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* Delete the allocation (reservation) of the memory. */
/* The char pointer x is still pointing to this address in memory though! */
free(x);
/* Same as malloc but here the allocated space is filled with null characters!*/
x = (char *) calloc(6, sizeof(x));
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* And delete the allocation again... */
free(x);
/* We can set the size at declaration time as well */
char xx[6];
xx[0] = 'H';
xx[1] = 'e';
xx[2] = 'l';
xx[3] = 'l';
xx[4] = 'o';
xx[5] = '\0';
printf("String \"%s\" at address: %d\n", xx, xx);

Зауважте, що ви все ще можете використовувати змінну x після того, як виконали вільну () виділену пам'ять, але ви не знаєте, що там є. Також зауважте, що два printf () можуть надавати вам різні адреси, оскільки немає гарантії, що другий розподіл пам'яті буде виконаний в тому ж просторі, що і перший.


8
Ваш приклад із 120 в ньому неправильний. Він використовує% c not% s, щоб не було помилок; він просто друкує малу літеру x. Більше того, ваше подальше твердження про те, що покажчик типу int є помилковим, і дуже погана порада дати програмісту C, який не має досвіду вказівників.
R .. GitHub СТОП ДОПОМОГАТИ ВІД

13
-1 Ви почали добре, але перший приклад - це неправильно. Ні, це не те саме. У першому випадку a- вказівник, а в другому a- масив. Я вже згадував про це? Це не те саме! Перевірте самі: порівняйте sizeof (a), спробуйте призначити нову адресу для масиву. Це не спрацює.
sellibitze

42
char* a = "Hello";і char a[] = "Hello";не однакові, вони зовсім інші. Один оголошує вказівник, інший - масив. Спробуйте a, sizeofі ви побачите різницю.
Патрік Шлютер

12
"Неправильно чи незаконно виконувати цю операцію printf". Це явно неправильно. Ця операція printf має невизначене поведінку. У багатьох реалізаціях це призведе до порушення доступу. Покажчики насправді не є типом int, вони фактично є покажчиками (і більше нічого).
Манкарсе

19
-1, Ви тут помилилися стільки фактів, і я просто поясню, що ви не заслуговуєте на кількість оновлених даних або статусу відповіді.
asveikau

50

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

У C ++ краща практика використання посилань, ніж покажчики. Хоча посилання по суті є покажчиками, C ++ певною мірою приховує факт і робить здається, що ви переходите за значенням. Це дозволяє легко змінити спосіб викликової функції отримувати значення, не змінюючи семантику передачі її.

Розглянемо наступні приклади:

Використання посилань:

public void doSomething()
{
    int i = 10;
    doSomethingElse(i);  // passes i by references since doSomethingElse() receives it
                         // by reference, but the syntax makes it appear as if i is passed
                         // by value
}

public void doSomethingElse(int& i)  // receives i as a reference
{
    cout << i << endl;
}

Використання покажчиків:

public void doSomething()
{
    int i = 10;
    doSomethingElse(&i);
}

public void doSomethingElse(int* i)
{
    cout << *i << endl;
}

16
Напевно, гарна ідея зазначити, що посилання є більш безпечними, оскільки ви не можете передати нульову посилання.
SpoonMeiser

26
Так, це, мабуть, найбільша перевага використання посилань. Дякуємо, що вказали на це. Не каламбур призначений :)
trshiv

2
Ви напевно можете пройти нульову посилання. Це просто не так просто, як передача нульового вказівника.
n0rd

1
узгоджуються з @ n0rd. Якщо ви думаєте, що не можете передати нульову посилання, ви помиляєтесь. Простіше пройти звисаючу посилання, ніж нульову посилання, але в кінцевому підсумку ви можете зробити це досить просто. Посилання - це не срібна куля, що захищає інженера від стрілянини в ногу. Дивіться це наживо .
WhozCraig

2
@ n0rd: Це явно невизначена поведінка.
Даніель Каміль Козар

42
  1. Покажчики дозволяють посилатися на один і той же простір в пам'яті з декількох місць. Це означає, що ви можете оновити пам'ять в одному місці, і зміни можна побачити в іншому місці вашої програми. Ви також заощадите простір, зможете ділитися компонентами у ваших структурах даних.
  2. Ви повинні використовувати вказівники в будь-якому місці, де потрібно отримати та передати навколо адреси певне місце в пам'яті. Ви також можете використовувати вказівники для навігації по масивах:
  3. Масив - це блок суміжної пам’яті, який був виділений певного типу. Ім'я масиву містить значення початкової точки масиву. Коли ви додасте 1, це переведе вас на друге місце. Це дозволяє писати петлі, які збільшують вказівник, який ковзає вниз по масиву, не маючи явного лічильника для використання в доступі до масиву.

Ось приклад на C:

char hello[] = "hello";

char *p = hello;

while (*p)
{
    *p += 1; // increase the character by one

    p += 1; // move to the next spot
}

printf(hello);

відбитки

ifmmp

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


because it takes the value for each character and increments it by one. Чи в представленні ascii чи як?
Едуардо С.

28

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

Найкраще пояснення покажчиків і арифметики покажчиків , які я читав в K & R в Мова програмування C . Гарною книгою для початку вивчення C ++ є C ++ Primer .


1
Дякую тобі! нарешті, практичне пояснення переваг використання стека! чи вказівник на позицію в масиві також покращує продуктивність доступу до значень @ та відносно вказівника?

Це, мабуть, найкраще пояснення, яке я прочитав. люди мають спосіб «надмірно ускладнювати» речі :)
Detilium

23

Дозвольте мені спробувати відповісти і на це.

Покажчики схожі на посилання. Іншими словами, це не копії, а скоріше спосіб посилання на початкове значення.

Перш за все, одне місце, де вам зазвичай доведеться багато використовувати покажчики, - це коли ви маєте справу з вбудованим обладнанням . Можливо, вам потрібно переключити стан цифрового вводу-виводу. Можливо, ви обробляєте переривання і вам потрібно зберегти значення у певному місці. Ви отримуєте картину. Однак якщо ви безпосередньо не маєте справу з обладнанням і просто цікавитеся, які типи використовувати, читайте далі.

Навіщо використовувати вказівники на відміну від звичайних змінних? Відповідь стає зрозумілішою, коли ви маєте справу зі складними типами, такими як класи, структури та масиви. Якщо ви використовували звичайну змінну, ви можете зробити копію (компілятори досить розумні, щоб запобігти цьому в деяких ситуаціях, і C ++ 11 також допомагає, але ми поки не будемо обговорювати це обговорення).

Тепер, що станеться, якщо ви хочете змінити початкове значення? Ви можете використовувати щось подібне:

MyType a; //let's ignore what MyType actually is right now.
a = modify(a); 

Це буде добре, і якщо ви точно не знаєте, чому ви використовуєте покажчики, не слід їх використовувати. Остерігайтеся причини "вони, мабуть, швидші". Запустіть власні тести, і якщо вони насправді швидші, то використовуйте їх.

Однак скажімо, ви вирішуєте проблему, на яку потрібно виділити пам'ять. Коли ви виділяєте пам'ять, вам потрібно розібрати її. Розподіл пам’яті може бути або не вдалим. Тут корисні вказівники - вони дозволяють перевірити наявність виділеного вами об'єкта , і вони дозволяють отримати доступ до об'єкта, на який була виділена пам'ять, шляхом де-посилання на покажчик.

MyType *p = NULL; //empty pointer
if(p)
{
    //we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if(p) //if the memory was allocated, this test will pass
{
    //we can do something with our allocated array
    for(size_t i=0; i!=50000; i++)
    {
        MyType &v = *(p+i); //get a reference to the ith object
        //do something with it
        //...
    }
    delete[] p; //we're done. de-allocate the memory
}

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

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

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

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

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

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

Тож я кажу, що неможливо створити недійсну посилання? Ні. Це цілком можливо, тому що C ++ дозволяє вам робити що завгодно. Це просто важче зробити ненавмисно, і ви будете вражені тим, скільки помилок ненавмисно :)


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

13

Ось дещо інший, але проникливий погляд на те, чому багато функцій C мають сенс: http://steve.yegge.googlepages.com/tour-de-babel#C

В основному, стандартна архітектура процесора - це архітектура Von Neumann, і надзвичайно корисно мати можливість посилатися на розташування елемента даних у пам’яті та робити арифметику з ним на такій машині. Якщо ви знаєте будь-який варіант мови складання, ви швидко зрозумієте, наскільки це важливо на низькому рівні.

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


12

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

Ще в старі часи DOS ви мали можливість отримати доступ до відеопам'яті відеокарти безпосередньо, оголосивши вказівник на:

unsigned char *pVideoMemory = (unsigned char *)0xA0000000;

Багато вбудованих пристроїв досі використовують цю техніку.


Не привід використовувати вказівник - пролітну структуру - яка також тримає довжину - набагато доречніше. У ці дні це є gsl::span, і скоро це буде std::span.
einpoklum

10

Значна частина покажчиків - це масиви (в C / C ++) - вони є адресами в пам'яті, і до них можна отримати доступ, як потрібний масив (у "звичайних" випадках).

Оскільки вони є об'єктом, вони невеликі: вони займають лише простір адреси. Оскільки вони невеликі, відправка їх на функцію коштує недешево. І тоді вони дозволяють цій функції працювати над фактичним елементом, а не копією.

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


8
Я думаю, що вказувати вказівники - це масиви, що оманливо. Дійсно, імена масивів - це покажчики const на перший елемент масиву. Просто тому, що ви можете отримати доступ до довільної точки, ніби її масив не означає, що це ... ви можете отримати порушення доступу :)
rmeador

2
Покажчики не є масивами. Читайте розділ 6 comp.lang.c FAQ .
Кіт Томпсон

Покажчики справді не є масивами. Крім того, мало використовуються і в сирому масиві - звичайно, не після C ++ 11 і std::array.
einpoklum

@einpoklum - покажчики досить близькі до масивів, щоб бути рівнозначними в операціях довідки та повторення через масиви :) .... також - C ++ 11 був 3 роки від випуску, коли ця відповідь була написана у 2008 році
warren

@warren: Покажчики не є ні масивами, ні близькими до масивів, вони просто розпадаються на масиви. Педагогічно недоцільно говорити людям, що вони схожі. Крім того, ви, звичайно, можете мати посилання на масиви, які не є типом вказівників і підтримують інформацію про розмір; дивіться тут
einpoklum

9

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

Покажчики корисні там, де вам потрібна висока продуктивність та / або компактний слід пам’яті.

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


9

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


Це привід використовувати посилання (або максимум - опорні обгортки), а не покажчики.
einpoklum

6

У C ++, якщо ви хочете використовувати підтиповий поліморфізм , вам доведеться використовувати вказівники. Дивіться цей пост: C ++ Поліморфізм без покажчиків .

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

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


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


Ні, вам не потрібно використовувати вказівники - ви можете використовувати посилання. А коли ви пишете "покажчики залучаються за лаштунками" - це безглуздо. gotoІнструкції також використовуються за кадром - в інструкціях цільової машини. Ми все ще не претендуємо на їх використання.
einpoklum

@ einpoklum-reinstateMonica Якщо у вас є набір об'єктів, над якими ви хочете діяти, призначаючи кожен елемент по черзі до тимчасової змінної та викликаючи поліморфний метод на цій змінній, то так, вам НЕОБХІДНА вказівник, оскільки ви не можете відновити посилання.
Сілдорет

Якщо у вас є посилання базового класу на похідний клас і виклик віртуального методу за цією посиланням, то виклик перехідного класу буде викликаний. Я помиляюся?
einpoklum

@ einpoklum-reinstateMonica Це правильно. Але ви не можете змінити, на який об’єкт посилається. Отже, якщо ви перебираєте список / набір / масив цих об'єктів, контрольна змінна не працюватиме.
Сілдорет

5

Тому що копіювання великих об'єктів по всіх місцях витрачає час і пам'ять.


4
І як вказівники допомагають у цьому? Я думаю, що людина, яка приїжджає з Java або .Net не знає різниці між стеком і купою, тому ця відповідь є досить марною ..
Mats Fredriksson

Ви можете пройти за посиланням. Це запобіжить копіювання.
Мартін Йорк

1
@MatsFredriksson - Замість передачі (копіювання) великої структури даних і копіювання результату знову, ви просто вказуєте, де він знаходиться в оперативній пам'яті, а потім безпосередньо змінюєте його.
Джон У

Тому не копіюйте великих об’єктів. Ніхто не сказав, що вам потрібні покажчики.
einpoklum

5

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

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

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

За допомогою покажчиків вам не доведеться оновлювати трикутники.

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


4

Необхідність покажчиків на C мовою описується тут

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

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

PS: Будь ласка, зверніться до посилання, наданого для детального пояснення із зразком коду.


Питання про С ++, ця відповідь - про C.
einpoklum

ні, питання позначено c AND c ++. Можливо, тег c не має значення, але він тут з самого початку.
Жан-Франсуа Фабре

3

У Java та C # всі посилання на об'єкт - це вказівники, річ із c ++ полягає в тому, що ви маєте більше контролю над тим, де ви вказуєте точки. Пам'ятайте З великою силою приходить велика відповідальність.


1

Що стосується вашого другого питання, то, як правило, не потрібно використовувати вказівники під час програмування, однак є один виняток із цього, і це коли ви створюєте загальнодоступний API.

Проблема з конструкціями C ++, які люди, як правило, використовують для заміни покажчиків, дуже залежать від набору інструментів, який ви використовуєте, що добре, коли у вас є весь контроль над вихідним кодом, однак, якщо ви збираєте статичну бібліотеку з візуальною студією 2008, наприклад і спробуйте використати його у візуальній студії 2010 року, ви отримаєте безліч помилок у зв’язці, оскільки новий проект пов'язаний з новою версією STL, яка не сумісна назад. Речі стають ще більш неприємними, якщо ви складете DLL і надаєте бібліотеку імпорту, яку користувачі використовують в іншому наборі інструментів, оскільки в цьому випадку ваша програма рано чи пізно вийде з ладу без видимих ​​причин.

Таким чином, для переміщення великих наборів даних з однієї бібліотеки в іншу ви можете розглянути можливість вказівника на масив до функції, яка повинна копіювати дані, якщо ви не хочете змушувати інших використовувати ті самі інструменти, які ви використовуєте . Хороша частина цього полягає в тому, що він навіть не повинен бути масивом у стилі С, ви можете використовувати std :: vector і давати вказівник, вказавши, наприклад, адресу першого елемента & вектора [0], і використовуйте std :: вектор для управління масивом внутрішньо.

Ще одна вагома причина для використання покажчиків у C ++ знову стосується бібліотек, розгляньте наявність DLL, який неможливо завантажити під час запуску програми, тому якщо ви використовуєте імпортну бібліотеку, то залежність не задоволена і програма виходить з ладу. Так відбувається, наприклад, коли ви даєте публічний api в dll поряд зі своїм додатком і хочете отримати доступ до нього з інших програм. У цьому випадку для використання API вам потрібно завантажити dll з його місця розташування (як правило, він знаходиться в ключі реєстру), а потім вам потрібно використовувати покажчик функції, щоб мати можливість викликати функції всередині DLL. Іноді люди, які роблять API, є досить приємними, щоб надати вам файл .h, який містить допоміжні функції для автоматизації цього процесу та дасть вам усі необхідні вказівники на функції,


1
  • У деяких випадках для використання функцій, що знаходяться у спільній бібліотеці (.DLL або .so), потрібні покажчики функцій. Сюди входить виконання різних мов, де часто надається інтерфейс DLL.
  • Виготовлення компіляторів
  • Створюючи наукові калькулятори, де у вас є масив чи векторна чи рядкова карта вказівників функцій?
  • Спроба безпосередньо змінити відеопам'ять - створити власний графічний пакет
  • Створення API!
  • Структури даних - покажчики посилань на вузли для спеціальних дерев, які ви створюєте

Є багато причин для покажчиків. Особливо важливим є керування іменем C, що особливо важливо для DLL, якщо ви хочете підтримувати сумісність між мовами.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.