Нещодавно я мав задоволення пояснювати покажчики початківцю програмування на С і натрапляв на наступні труднощі. Це може не здатися проблемою, якщо ви вже знаєте, як користуватися покажчиками, але спробуйте зрозуміти наступний приклад чітко:
int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);
Для абсолютного початківця вихід може бути дивовижним. У другому рядку він / вона щойно оголосив * bar для & foo, але у рядку 4 виявляється * bar насправді foo замість & foo!
Плутанина, можна сказати, випливає з неоднозначності символу *: У другому рядку він використовується для оголошення вказівника. У рядку 4 він використовується як одинарний оператор, який отримує значення, на яке вказує вказівник. Дві різні речі, правда?
Однак це "пояснення" зовсім не допомагає новачку. Він вводить нову концепцію, вказуючи на тонку невідповідність. Це не може бути правильним способом цього навчити.
Отже, як це пояснили Керніган та Річі?
Одинарний оператор * - це оператор опосередкування або перенаправлення; при застосуванні до вказівника він отримує доступ до об'єкта, на який вказує вказівник. […]
Декларація вказівника ip,
int *ip
призначена як мнемонічна; це говорить, що вираз*ip
є цілим. Синтаксис оголошення для змінної імітує синтаксис виразів, у яких може відображатися змінна .
int *ip
слід читати як " *ip
повернеться int
"? Але чому тоді призначення після декларації не відповідає цій схемі? Що робити, якщо новачок хоче ініціалізувати змінну? int *ip = 1
(читайте: *ip
повернеться int
та int
є 1
) не буде працювати, як очікувалося. Концептуальна модель просто не здається узгодженою. Я щось тут пропускаю?
Редагувати: Тут спробували узагальнити відповіді .
*
в декларації є лексемою, що означає "оголосити вказівник", у виразах - оператор перенаправлення, і що ці два репрезентують різні речі, які, мабуть, мають однаковий символ (те саме, що оператор множення - той самий символ, різний зміст). Це заплутано, але все, що відрізняється від фактичного стану справ, буде ще гіршим.
int* bar
робить більш очевидним, що зірка насправді є частиною типу, а не частиною ідентифікатора. Звичайно, це створює вам різні проблеми з такими неінтуїтивними матеріалами, як int* a, b
.
*
може мати два різних значення залежно від контексту. Так само, як та сама буква може вимовлятися по-різному, залежно від слова, в якому важко навчитися говорити на багатьох мовах. Якби кожна концепція / операція мала свій власний символ, нам знадобляться набагато більші клавіатури, тому символи переробляються, коли це має сенс.
int* p
), попереджаючи свого учня проти використання кількох декларацій в одному рядку, коли задіяні покажчики. Коли студент повністю зрозумів поняття покажчиків, поясніть студенту, що int *p
синтаксис is є рівнозначним, а потім поясніть проблему декількома деклараціями.