Почему звездочка перед именем переменной, а не после типа? - PullRequest
163 голосов
/ 29 декабря 2008

Почему большинство программистов на C называют переменные следующим образом:

int *myVariable;

а не так:

int* myVariable;

Оба действительны. Мне кажется, что звездочка является частью типа, а не частью имени переменной. Кто-нибудь может объяснить эту логику?

Ответы [ 12 ]

219 голосов
/ 29 декабря 2008

Они ТОЧНО эквивалентны. Однако в

int *myVariable, myVariable2;

Кажется очевидным, что myVariable имеет тип int *, тогда как myVariable2 имеет тип int . В

int* myVariable, myVariable2;

может показаться очевидным, что оба имеют тип int *, но это неверно, поскольку myVariable2 имеет тип int .

Следовательно, первый стиль программирования более интуитивен.

106 голосов
/ 29 декабря 2008

Если вы посмотрите на это по-другому, *myVariable имеет тип int, что имеет некоторый смысл.

39 голосов
/ 29 декабря 2008

Поскольку * в этой строке более тесно связан с переменной, чем с типом:

int* varA, varB; // This is misleading

Как @Lundin указывает ниже, const добавляет еще больше тонкостей для размышлений. Вы можете полностью обойти это, объявив одну переменную на строку, которая никогда не бывает неоднозначной:

int* varA;
int varB;

Сложно найти баланс между чистым кодом и кратким кодом & mdash; дюжина лишних строк int a; тоже не годится. Тем не менее, я по умолчанию использую одно объявление на строку и беспокоюсь о том, чтобы объединить код позже.

24 голосов
/ 22 июля 2015

Здесь никто до сих пор не упомянул, что эта звездочка на самом деле является « оператором разыменования » в C.

*a = 10;

Строка выше не означает, что я хочу назначить 10 для a, это означает, что я хочу назначить 10 для любой области памяти, на которую указывает a. И я никогда не видел, чтобы кто-нибудь писал

* a = 10;

ты? Так что оператор разыменования почти всегда пишется без пробела. Это, вероятно, чтобы отличить его от умножения, разбитого на несколько строк:

x = a * b * c * d
  * e * f * g;

Здесь *e будет вводить в заблуждение, не так ли?

Хорошо, теперь, что на самом деле означает следующая строка:

int *a;

Большинство людей скажут:

Это означает, что a является указателем на значение int.

Это технически правильно, большинству людей нравится видеть / читать его таким образом, и именно так его определяют современные стандарты C (обратите внимание, что язык C сам по себе предшествует всем стандартам ANSI и ISO). Но это не единственный способ взглянуть на это. Вы также можете прочитать эту строку следующим образом:

Разыменованное значение a имеет тип int.

Так что на самом деле звездочка в этом объявлении также может рассматриваться как оператор разыменования, что также объясняет его размещение. И то, что a является указателем, на самом деле вообще не объявляется, это подразумевается тем фактом, что единственное, что вы можете разыменовать - это указатель.

Стандарт C определяет только два значения для оператора *:

  • оператор косвенного обращения
  • оператор умножения

И косвенность - это просто одно значение, для объявления указателя нет никакого дополнительного значения, есть только косвенность, то есть то, что делает операция разыменования, она выполняет косвенный доступ, так же как и внутри оператора вроде int *a; является косвенным доступом (* означает косвенный доступ), и, следовательно, второе приведенное выше утверждение намного ближе к стандарту, чем первое.

17 голосов
/ 14 октября 2011

Я собираюсь выйти здесь на конечность и сказать, что есть прямой ответ на этот вопрос , как для объявлений переменных, так и для параметров и типов возврата, то есть звездочка должна идти рядом с именем: int *myVariable;. Чтобы понять почему, посмотрите, как вы объявляете другие типы символов в C:

int my_function(int arg); для функции;

float my_array[3] для массива.

Общий шаблон, именуемый декларацией после использования , состоит в том, что тип символа разбивается на часть перед именем, а части вокруг имени, и эти части вокруг имени имитируют синтаксис, который вы использовали бы для получения значения типа слева:

int a_return_value = my_function(729);

float an_element = my_array[2];

и: int copy_of_value = *myVariable;.

C ++ создает гаечный ключ при работе со ссылками, поскольку синтаксис в точке, где вы используете ссылки, идентичен синтаксису типов значений, поэтому вы можете утверждать, что C ++ использует другой подход к C. С другой стороны, C ++ сохраняет то же поведение C в случае указателей, так что ссылки в этом отношении действительно являются нечетными.

11 голосов
/ 29 декабря 2008

Это просто вопрос предпочтений.

Когда вы читаете код, различать переменные и указатели легче во втором случае, но это может привести к путанице, когда вы помещаете как переменные, так и указатели общего типа в одну строку (что часто не рекомендуется руководящие принципы проекта, потому что снижает читабельность).

Я предпочитаю объявлять указатели с соответствующими знаками рядом с именем типа, например,

int* pMyPointer;
7 голосов
/ 08 марта 2016

Великий гуру однажды сказал: «Прочтите это по-компиляторно, вы должны».

http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835

Конечно, это было на тему размещения констант, но здесь применяется то же правило.

Компилятор читает это как:

int (*a);

не как:

(int*) a;

Если вы привыкнете размещать звездочку рядом с переменной, это облегчит чтение ваших объявлений. Это также избегает глазных пятен, таких как:

int* a[10];

- Правка -

Чтобы точно объяснить, что я имею в виду, когда я говорю, что он анализируется как int (*a), это означает, что * более тесно связывается с a, чем с int, во многом так, как в выражении 4 + 3 * 7 3 более тесно связывается с 7, чем с 4 из-за более высокого приоритета *.

С извинениями за искусство ascii, краткий обзор А.С.Т. для разбора int *a выглядит примерно так:

      Declaration
      /         \
     /           \
Declaration-      Init-
Secifiers       Declarator-
    |             List
    |               |
    |              ...
  "int"             |
                Declarator
                /       \
               /        ...
           Pointer        \
              |        Identifier
              |            |
             "*"           |
                          "a"

Как ясно показано, * более тесно связывается с a, поскольку их общий предок Declarator, в то время как вам нужно пройти весь путь вверх по дереву до Declaration, чтобы найти общего предка, который включает int.

5 голосов
/ 08 марта 2016

В K & R оператор указателя располагается рядом с именем переменной, а не с типом. Лично я считаю эту книгу высшим авторитетом в Библии. Также, исходя из фона Objective-C, я следую соглашению * name.

2 голосов
/ 18 октября 2012

Для объявления нескольких указателей в одной строке я предпочитаю int* a, * b;, который более интуитивно объявляет "a" как указатель на целое число и не смешивает стили, когда аналогично объявляется "b" Как кто-то сказал, я бы не объявлял два разных типа в одном и том же утверждении.

2 голосов
/ 29 декабря 2008

Потому что это имеет больше смысла, когда у вас есть объявления вроде:

int *a, *b;
...