Розміщення зірочки в деклараціях покажчиків


92

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

Як щодо цих прикладів:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

Зараз, наскільки я розумію, перші три випадки роблять те саме: Test - це не int, а вказівник на один.

Другий приклад трохи складніший. У випадку 4 і test, і test2 будуть вказівниками на int, тоді як у випадку 5 лише test є покажчиком, тоді як test2 - "справжній" int. А як щодо випадку 6? Те саме, що у випадку 5?


10
У C / C ++ пробіли не змінюють значення.
Султан

19
7. int*test;?
Jin Kwon

3
+1, тому що я думав запитати лише про 1 - 3. Читання цього питання навчило мене чомусь про 4 - 6, про що я ніколи не думав.
надзвичайнодосконалий

@Sulthan Це правда в 99% випадків, але не завжди. У верхній частині моєї голови був тип шаблонного типу в вимозі до простору шаблонного типу (до C ++ 11). У повинні були бути написані таким чином , не повинні розглядатися в якості правого зсуву. Foo<Bar<char>>>>> >
AnorZaken

3
@AnorZaken Ви маєте рацію, це досить старий коментар. Існує кілька ситуацій, коли пробіл змінює значення, наприклад, ++оператор приросту не може бути розділений пробілом, ідентифікатори не можуть бути розділені пробілом (і результат може все ще бути законним для компілятора, але з невизначеною поведінкою виконання). Точні ситуації дуже важко визначити, враховуючи синтаксичний безлад, яким є C / C ++.
Султан

Відповіді:


129

4, 5 і 6 - це одне і те ж, лише тест - це покажчик. Якщо ви хочете два вказівники, вам слід використовувати:

int *test, *test2;

Або, ще краще (щоб все було зрозуміло):

int* test;
int* test2;

3
Тож випадок 4 насправді - це тоді смертельна пастка? Чи існує якась специфікація чи подальше прочитання, що пояснює, чому int * test, test2 лише робить першу змінну покажчиком?
Майкл Штум

8
@ Michael Stum Це C ++, тож ти справді думаєш, що є логічне пояснення?
Джо Філліпс,

6
Прочитайте K&R (Мова програмування C). Це все дуже чітко пояснює.
Ферруччо

8
Випадки 4, 5 та 6 - це "пастки смерті". Це одна з причин, чому багато юнаків стилю C / C ++ пропонують лише одну декларацію для кожного висловлювання.
Michael Burr

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

45

Білий простір навколо зірочок не має значення. Всі троє означають одне і те ж:

int* test;
int *test;
int * test;

Символ " int *var1, var2" - це злий синтаксис, який просто призначений спантеличити людей і якого слід уникати. Він розширюється до:

int *var1;
int var2;

16
Не забувайте int*test.
Mateen Ulhaq,

1
простір до або після зірочки - це лише питання естетики. Однак стандарт кодування Google поєднується з int *test( google-styleguide.googlecode.com/svn/trunk/… ). Просто будьте послідовними

@SebastianRaschka Посібник із стилів Google C ++ прямо передбачає розміщення зірочок. Можливо, це змінилося з тих пір, як ви його прочитали.
Джаред Бек,

33

Багато вказівок щодо кодування рекомендують оголошувати лише одну змінну на рядок . Це дозволяє уникнути будь-якої плутанини, якої ви мали до того, як задати це запитання. Більшість програмістів на C ++, з якими я працював, дотримуються цього.


Трохи осторонь я знаю, але щось, що мені здалося корисним, - читати декларації назад.

int* test;   // test is a pointer to an int

Це починає працювати дуже добре, особливо коли ви починаєте оголошувати покажчики const, і стає складно дізнатися, чи це вказівник const, чи це річ, на яку вказує вказівник, це const.

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const

Хоча "одна змінна на рядок" здається корисною, ми все ще не до кінця вирішили ситуацію, коли зірочка знаходиться лівіше або більше праворуч. Я повністю впевнений, що в коді з дикої природи переважає один варіант; трохи подібно до того, як деякі країни їдуть правою стороною, а інші - неправильним, наприклад, Великобританія. ;-)
shevy

На жаль, з моїх пригод у дикій природі я бачу багато обох стилів. Зараз в моїй команді ми використовуємо clang-формат із стилем, про який ми домовились. Це принаймні означає, що весь код, який створює наша команда, має однаковий стиль для того, куди йде пробіл.
Скотт Ленгем,

33

Використовуйте "Правило спіралі за годинниковою стрілкою", щоб допомогти розібрати декларації C / C ++;

Існує три простих кроки:

  1. Починаючи з невідомого елемента, рухайтеся по спіралі / за годинниковою стрілкою; при зустрічі з наступними елементами замініть їх відповідними англійськими висловлюваннями:

    [X]або []: масив X розмір ... або масив невизначений розмір ...

    (type1, type2): передача функції type1 і type2 повертається ...

    *: вказівник (и) на ...

  2. Продовжуйте робити це по спіралі / за годинниковою стрілкою, поки не будуть покриті всі жетони.
  3. Завжди вирішуйте що-небудь у дужках спочатку!

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


4
Це виглядає лякаюче та досить жахливе, вибачте сказати.
Джо Філліпс,

7
це так, але це, здається, досить гарне пояснення деяких складніших конструкцій
Майкл Штум

@ d03boy: Немає сумнівів - декларації C / C ++ можуть бути кошмаром.
Michael Burr

2
"Спіраль" не має жодного сенсу, а тим більше "годинникова стрілка". Я волів би назвати це "правилом праворуч-ліворуч", оскільки синтаксис не змушує вас виглядати праворуч-знизу-ліворуч-зверху, лише праворуч-ліворуч.
Руслан

Я дізнався це як правило "право-ліво-право". Люди на C ++ часто люблять робити вигляд, що вся інформація про тип знаходиться зліва, що веде до int* x;стилю, а не до традиційного int *x;стилю. Звичайно, інтервал для компілятора не має значення, але це впливає на людей. Заперечення фактичного синтаксису призводить до правил стилю, які можуть дратувати та бентежити читачів.
Адріан Маккарті,

12

Як згадували інші, 4, 5 і 6 однакові. Часто люди використовують ці приклади, щоб аргументувати *приналежність до змінної замість типу. Хоча це питання стилю, є деякі суперечки щодо того, чи варто думати і писати це так:

int* x; // "x is a pointer to int"

або таким чином:

int *x; // "*x is an int"

FWIW Я перебуваю в першому таборі, але причина, за якою інші аргументують другу форму, полягає в тому, що вона (в основному) вирішує цю конкретну проблему:

int* x,y; // "x is a pointer to int, y is an int"

що потенційно вводить в оману; замість цього ви б написали або

int *x,y; // it's a little clearer what is going on here

або якщо ви дійсно хочете два вказівники,

int *x, *y; // two pointers

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


6
це брехливо, що ти називаєш int *MyFunc(void)? a *MyFunc- це функція, яка повертає an int? немає. Очевидно, що ми повинні писати int* MyFunc(void), і сказати MyFunc, це функція, що повертає a int*. Отже, для мене це зрозуміло, правила розбору граматики C та C ++ просто неправильні для оголошення змінних. вони повинні включати кваліфікацію покажчика як частину загального типу для всієї послідовності коми.
v.oddou

1
Але *MyFunc() цеint . Проблема синтаксису С полягає у змішуванні синтаксису префіксу та постфіксу - якби використовувався лише постфікс, не було б плутанини.
Antti Haapala

1
Перший табір бореться із синтаксисом мови, що призводить до заплутаних конструкцій типу int const* x;, які я вважаю такими ж оманливими a * x+b * y.
Адріан Маккарті


5

У 4, 5 і 6 testзавжди є покажчиком і test2не є покажчиком. Пробіл (майже) ніколи не є значущим у C ++.


3

На мій погляд, відповідь - ОБИН, залежно від ситуації. Як правило, IMO, краще ставити зірочку поруч з ім'ям вказівника, а не над типом. Порівняйте, наприклад:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

Чому другий випадок суперечливий? Тому що, наприклад, int x,y;оголошує дві змінні одного типу, але тип згадується лише один раз у оголошенні. Це створює прецедент та очікувану поведінку. І int* pointer1, pointer2;несумісний з цим, оскільки він оголошує pointer1як покажчик, але pointer2є цілою змінною. Очевидно схильний до помилок і, отже, його слід уникати (ставлячи зірочку поруч із іменем вказівника, а не типом).

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

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

Нарешті, в деяких випадках може бути безперечно зрозуміліше поставити зірочку поруч із назвою типу , наприклад:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight


2

Обґрунтуванням на С є те, що ви оголошуєте змінні так, як ви їх використовуєте. Наприклад

char *a[100];

каже, що *a[42]це буде a char. І a[42]вказівник на символ. І таким чином aє масив вказівників на символи.

Це тому, що автори оригінальних компіляторів хотіли використовувати той самий синтаксичний аналізатор для виразів та оголошень. (Не дуже розумна причина для вибору дизайну багажу)


Проте написання char* a[100]; також виводить, що *a[42];це буде покажчиком charі a[42];символом char.
yyny

Ну, ми всі робимо однакові висновки, тільки порядок різниться.
Мішель Бійо

Цитата: "говорить, що * a [42] буде символом. А [42] покажчиком char". Ви впевнені, що не все навпаки?
deLock

Якщо ви віддаєте перевагу іншому шляху, скажіть a[42], це charпокажчик, і *a[42]це символ.
Мішель Бійо,

2

Я б сказав, що початковою умовою було поставити зірочку на стороні імені вказівника (права частина оголошення

  • в мові програмування Денніс М. Рітчі зірки на правій стороні декларації.

  • переглянувши вихідний код Linux за адресою https://github.com/torvalds/linux/blob/master/init/main.c, ми побачимо, що зірка також знаходиться з правого боку.

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


Ну - парсер, схоже, допускає будь-який варіант, але якщо Денніс і Лінус кажуть, що він повинен бути з правого боку, це цілком переконливо. Але все-таки нам якось не вистачає обгрунтування, а потім і пояснення, чому це робиться. Це трохи схоже на ситуацію табуляції та космосу - за винятком тієї, яку вирішили, бо люди, які використовують пробіли, а не вкладки, заробляють більше грошей, згідно StackOverflow ... :-)
shevy

1

Покажчик є модифікатором типу. Краще читати їх справа наліво, щоб краще зрозуміти, як зірочка змінює тип. "int *" можна прочитати як "вказівник на int". У кількох оголошеннях ви повинні вказати, що кожна змінна є покажчиком, інакше вона буде створена як стандартна змінна.

1,2 та 3) Тест має тип (int *). Пробіл не має значення.

4,5 і 6) Тест типу (int *). Test2 має тип int. Знову пробіли не мають значення.


-3

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

Візьмемо для прикладу:

int const bla;

Const стосується слова "int". Те саме стосується зірочок вказівників, вони застосовуються до ключового слова ліворуч від них. А власне ім'я змінної? Так, це задекларовано тим, що від нього залишилося.


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