Многие аргументы в этой теме носят субъективный характер, а аргумент о том, что «звезда связана с именем переменной», наивен. Вот несколько аргументов, которые не являются просто мнениями:
Спецификаторы типа забытого указателя
Формально «звезда» не принадлежит ни типу, ни имени переменной, она является частью своего собственного грамматического элемента с именем указатель . Формальный синтаксис C (ISO 9899: 2018):
(6.7) объявление:
спецификаторы объявлений список инициаторов объявлений opt ;
Где спецификаторы объявлений содержит тип (и хранилище), а список инициаторов объявлений содержит указатель и имя переменной. Что мы увидим, если разберем синтаксис этого списка объявлений далее:
(6.7.6) декларатор:
указатель опция прямое объявление
...
(6.7.6) указатель:
*
список квалификаторов типов opt
*
список квалификаторов типов opt указатель
Если декларатор - это полное объявление, прямой декларатор - это идентификатор (имя переменной), а указатель - это звезда, за которой следует необязательный список квалификаторов типов, принадлежащий самому указателю.
То, что делает различные аргументы стиля о том, что «звезда принадлежит переменной» несовместимо, заключается в том, что они забыли об этих квалификаторах типа указателя. int* const x
, int *const x
или int*const x
?
Рассмотрим int *const a, b;
, какие типы a
и b
? Не так очевидно, что «звезда принадлежит переменной» больше. Скорее, можно было бы задуматься о том, где находится const
.
Вы можете определенно аргументировать, что звезда относится к определителю типа указателя, но не намного дальше.
Список квалификаторов типов для указателя может вызвать проблемы у тех, кто использует стиль int *a
. Те, кто использует указатели внутри typedef
(что мы не должны, очень плохая практика!) И думают, что «звезда принадлежит имени переменной», как правило, пишут эту очень тонкую ошибку:
/*** bad code, don't do this ***/
typedef int *bad_idea_t;
...
void func (const bad_idea_t *foo);
Это компилируется чисто. Теперь вы можете подумать, что код сделан правильно. Не так! Этот код случайно является фальшивой константностью.
Тип foo
на самом деле int*const*
- самый внешний указатель был сделан только для чтения, а не наведен на данные. Так что внутри этой функции мы можем сделать **foo = n;
, и она изменит значение переменной в вызывающей программе.
Это потому, что в выражении const bad_idea_t *foo
, *
здесь не принадлежит имени переменной! В псевдокоде это объявление параметра следует читать как const (bad_idea_t *) foo
и , а не как (const bad_idea_t) *foo
. В этом случае звезда относится к скрытому типу указателя - тип является указателем, а указатель с константой записывается как *const
.
Но тогда корень проблемы в вышеприведенном примере - это практика скрытия указателей за typedef
, а не за *
стилем.
Относительно объявления нескольких переменных в одной строке
Объявление нескольких переменных в одной строке широко признано плохой практикой 1) . CERT-C подытоживает это как:
DCL04-C. Не объявляйте более одной переменной на объявление
Просто читая по-английски, здравый смысл соглашается с тем, что декларация должна быть одна декларация.
И не имеет значения, являются ли переменные указателями или нет. Объявление каждой переменной в одной строке делает код более понятным почти в каждом случае.
Таким образом, аргумент о том, что программист запутался из-за int* a, b
, плох. Корень проблемы - использование нескольких деклараторов, а не размещение *
. Независимо от стиля, вы должны написать это вместо:
int* a; // or int *a
int b;
Другим обоснованным, но субъективным аргументом было бы то, что для int* a
тип a
не вызывает вопросов int*
, поэтому звезда относится к определителю типа.
Но в основном я пришел к выводу, что многие из приведенных здесь аргументов просто субъективны и наивны. Вы не можете в действительности дать веский аргумент для любого стиля - это действительно вопрос субъективных личных предпочтений.
1) CERT-C DCL04-C .