Почему большинство программистов на C называют переменные так:
int *myVariable;
а не так:
int* myVariable;
Оба действительны. Мне кажется, что звездочка является частью типа, а не частью имени переменной. Кто-нибудь может объяснить эту логику?
c
pointers
variables
naming-conventions
WBlasko
источник
источник
typedefs
, но это добавит ненужную сложность, ИМХО.Ответы:
Они ТОЧНО эквивалентны. Однако в
Кажется очевидным, что myVariable имеет тип int * , а myVariable2 имеет тип int . В
может показаться очевидным, что оба имеют тип int * , но это неверно, так как
myVariable2
имеет тип int .Поэтому первый стиль программирования более интуитивен.
источник
int* someVar
личных проектов. Это имеет больше смысла.int[10] x
. Это просто не синтаксис Си. Грамматика явно разбирается как:,int (*x)
а не как(int *) x
, поэтому размещение звездочки слева просто вводит в заблуждение и основано на неправильном понимании синтаксиса объявления Си.int* myVariable; int myVariable2;
вместо этого.Если вы посмотрите на это по-другому,
*myVariable
это типint
, который имеет некоторый смысл.источник
myVariable
может быть NULL, в этом случае*myVariable
вызывает ошибку сегментации, но нет типа NULL.int x = 5; int *pointer = &x;
потому что он предполагает, что мы устанавливаем для int*pointer
значение, а неpointer
само значение.Потому что * в этой строке привязывается ближе к переменной, чем к типу:
Как указывает @Lundin ниже, const добавляет еще больше тонкостей для размышлений. Вы можете полностью обойти это, объявив одну переменную на строку, что никогда не бывает двусмысленным:
Трудно установить баланс между чистым кодом и кратким кодом - дюжина избыточных строк
int a;
тоже не годится. Тем не менее, я по умолчанию одно объявление на строку и беспокоюсь о комбинировании кода позже.источник
int *const a, b;
. Где * "связать"? Типa
isint* const
, так как вы можете сказать, что * принадлежит переменной, когда она является частью самого типа?Что-то еще никто не упомянул здесь, это то, что эта звездочка на самом деле является « оператором разыменования » в C.
Линия выше не означает , что я хочу , чтобы назначить
10
наa
это означает , что я хочу , чтобы назначить10
в любое место памятиa
указывает. И я никогда не видел, чтобы кто-нибудь писалу тебя есть? Таким образом, оператор разыменования почти всегда пишется без пробела. Это, вероятно, чтобы отличить его от умножения, разбитого на несколько строк:
Это
*e
могло бы ввести в заблуждение, не так ли?Хорошо, теперь, что на самом деле означает следующая строка:
Большинство людей скажут:
Это означает, что
a
это указатель наint
значение.Это технически правильно, большинству людей нравится видеть / читать его таким образом, и именно так его определяют современные стандарты C (обратите внимание, что язык C сам по себе предшествует всем стандартам ANSI и ISO). Но это не единственный способ взглянуть на это. Вы также можете прочитать эту строку следующим образом:
Значение разыменования
a
имеет типint
.Таким образом, на самом деле звездочка в этом объявлении также может рассматриваться как оператор разыменования, что также объясняет его размещение. И этот
a
указатель на самом деле не объявлен вообще, это подразумевается тем фактом, что единственное, что вы можете разыменовать, это указатель.Стандарт C определяет только два значения для
*
оператора:И косвенность - это просто одно значение, нет никакого дополнительного значения для объявления указателя, есть просто косвенность, то есть то, что делает операция разыменования, она выполняет косвенный доступ, так же как и внутри оператора,
int *a;
такого как косвенный доступ (*
означает косвенный доступ) и, следовательно, второе утверждение, приведенное выше, намного ближе к стандарту, чем первое.источник
int a, *b, (*c)();
то вроде «объявляю следующие объекты какint
: объектa
, объект , на который указываетb
, и объект, возвращенный из функции, на которую указываетc
».*
inint *a;
не является оператором и не разыменовываетсяa
(что даже не определено)*
(это дает лишь значение в суммарном выражении, а не в*
пределах выражения!). Там написано, что "int a;" объявляет указатель, что он делает, никогда не заявлял иначе, но без значения*
, читаемого как * значение разыменованногоa
является int , все еще полностью допустимо, поскольку это имеет тот же фактический смысл. Ничто, на самом деле ничто из написанного в 6.7.6.1 не противоречит этому утверждению.int *a;
это декларация, а не выражение.a
не разыменовываетсяint *a;
.a
даже не существует в тот момент,*
когда обрабатывается. Как вы думаетеint *a = NULL;
, это ошибка, потому что она разыменовывает нулевой указатель?Я собираюсь выйти на конечности здесь и сказать , что есть прямой ответ на этот вопрос , как для объявления переменных и параметров и возвращаемых типов, которые в том , что звездочка должна идти рядом с названием:
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 в случае указателей, так что ссылки действительно выделяются как странные в этом отношении.
источник
Это просто вопрос предпочтений.
Когда вы читаете код, различать переменные и указатели легче во втором случае, но это может привести к путанице, когда вы помещаете и переменные и указатели общего типа в одну строку (что само по себе часто не рекомендуется руководящими принципами проекта, потому что уменьшается читаемость).
Я предпочитаю объявлять указатели с соответствующими знаками рядом с именем типа, например
источник
Один великий гуру однажды сказал: «Прочтите это по пути компилятора».
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
Конечно, это было связано с темой размещения констант, но здесь применяется то же правило.
Компилятор читает это как:
не как:
Если вы привыкнете размещать звездочку рядом с переменной, это облегчит чтение ваших объявлений. Это также избегает глазных пятен, таких как:
-- Редактировать --
Чтобы точно объяснить, что я имею в виду, когда я говорю, что он анализируется как
int (*a)
, это означает, что он*
более тесно связан сa
чемint
, во многом так, как это выражается в выражении4 + 3 * 7
3
более тесно,7
чем с ним4
из-за более высокого приоритета*
.С извинениями за ascii art, краткий обзор AST для разбора
int *a
выглядит примерно так:Как ясно показано,
*
связывается более тесноa
с их общим предкомDeclarator
, в то время как вам нужно пройти весь путь вверх по дереву,Declaration
чтобы найти общего предка, который включает в себяint
.источник
(int*) a
.int
в таком случае. Шаг 2. Прочитайте декларацию, включая любые украшения типа.*a
в таком случае. Шаг 3 Прочитайте следующий символ. Если запятая, уничтожьте ее и вернитесь к шагу 2. Если точка с запятой остановится Если что-нибудь еще, сгенерируйте синтаксическую ошибку. ...int* a, b;
и получить пару указателей. Я хочу подчеркнуть, что*
связывается с переменной и анализируется с ней, а не с типом, чтобы сформировать «базовый тип» объявления. Это также одна из причин того, что typedefs были введены дляtypedef int *iptr;
iptr a, b;
создания пары указателей. Используя typedef, вы можете привязать*
кint
.Declaration-Specifier
с "украшениями" вDeclarator
для получения окончательного типа для каждой переменной. Однако это не «MOVE» декорации к спецификатор декларации в противном случаеint a[10], b;
будет производить совершенно нелепые результаты, он разбирает ,int *a, b[10];
какint
*a
,
b[10]
;
. Нет другого способа описать это, что имеет смысл.int*a
в конце читается компилятором как: «a
имеет типint*
». Это было то, что я имею в виду с моим оригинальным комментарием.Потому что это имеет больше смысла, когда у вас есть такие объявления, как:
источник
int* a, b;
сделалb
указатель наint
. Но они этого не сделали. И не без причины. Под вашей предложенной системой, какой типb
в следующей декларацииint* a[10], b;
:?Для объявления нескольких указателей в одной строке я предпочитаю, чтобы
int* a, * b;
более интуитивно объявлять «a» как указатель на целое число и не смешивать стили, когда аналогично объявляется «b». Как кто-то сказал, я бы в любом случае не объявлял два разных типа в одном и том же утверждении.источник
Люди, которые предпочитают
int* x;
, пытаются втиснуть свой код в вымышленный мир, где тип находится слева, а идентификатор (имя) - справа.Я говорю «вымышленный», потому что:
Это может звучать безумно, но вы знаете, что это правда. Вот некоторые примеры:
int main(int argc, char *argv[])
означает «main
это функция, которая принимаетint
массив и указателей наchar
и возвращаетint
». Другими словами, большая часть информации о типе находится справа. Некоторые люди думают, что объявления функций не учитываются, потому что они как-то «особенные». Хорошо, давайте попробуем переменную.void (*fn)(int)
означаетfn
, что указатель на функцию, которая принимаетint
и ничего не возвращает.int a[10]
объявляет 'a' как массив из 10int
с.pixel bitmap[height][width]
,Понятно, что у меня есть несколько примеров, которые содержат много информации о типах, чтобы понять мою точку зрения. Есть много объявлений, где большинство - если не все - типа слева, например
struct { int x; int y; } center
.Этот синтаксис декларации вырос из желания K & R иметь декларации, отражающие использование. Чтение простых объявлений интуитивно понятно, а чтение более сложных можно освоить, изучая правило справа налево и право (иногда называют спиральным правилом или просто правилом справа налево).
C достаточно прост, что многие программисты на C воспринимают этот стиль и пишут простые объявления как
int *p
.В C ++ синтаксис стал немного более сложным (с классами, ссылками, шаблонами, перечисляемыми классами), и, как реакция на эту сложность, вы увидите больше усилий по отделению типа от идентификатора во многих объявлениях. Другими словами, вы можете увидеть больше
int* p
объявлений -style, если вы проверяете большую часть кода C ++.В любом языке вы всегда можете иметь тип в левой части объявлений переменных : (1) никогда не объявлять несколько переменных в одном выражении и (2) использовать
typedef
s (или объявления псевдонимов, которые, как ни странно, ставят псевдоним идентификаторы слева от типов). Например:источник
(*fn)
указатель,fn
а не возвращаемый тип.Я уже ответил на аналогичный вопрос в CP, и, поскольку никто не упомянул об этом, здесь я также должен указать, что C - это язык свободного формата , какой бы стиль вы ни выбрали, он подходит, в то время как анализатор может различать каждый токен. Эта особенность С приводит к особому типу соревнований, называемых С обфускацией .
источник
Многие аргументы в этой теме носят субъективный характер, а аргумент о «звезде, связанной с именем переменной» наивен. Вот несколько аргументов, которые не просто мнения:
Забытые указатели типа указателя
Формально «звезда» не принадлежит ни типу, ни имени переменной, она является частью своего грамматического элемента с именем указатель . Формальный синтаксис C (ISO 9899: 2018):
Где спецификатор объявления содержит тип (и хранилище), а список инициаторов объявления содержит указатель и имя переменной. Что мы увидим, если мы разберем синтаксис этого списка объявлений дальше:
Если декларатор - это полное объявление, прямой декларатор - это идентификатор (имя переменной), а указатель - это звезда, за которой следует необязательный список квалификаторов типов, принадлежащий самому указателю.
То, что делает различные аргументы стиля о том, что «звезда принадлежит переменной» несовместимо, так это то, что они забыли об этих квалификаторах типа указателя.
int* const x
,int *const x
Илиint*const x
?Рассмотрим
int *const a, b;
, какие бывают типыa
иb
? Не так очевидно, что «звезда принадлежит переменной» больше. Скорее, можно было бы задуматься о том, где онconst
принадлежит.Вы можете определенно аргументировать, что звезда относится к определителю типа указателя, но не намного дальше.
Список спецификаторов типов для указателя может вызвать проблемы у тех, кто использует
int *a
стиль. Те, кто использует указатели внутриtypedef
(что мы не должны делать, очень плохая практика!) И думают, что «звезда принадлежит имени переменной», как правило, пишут эту очень тонкую ошибку:Это компилируется чисто. Теперь вы можете подумать, что код сделан правильно. Не так! Этот код случайно является фальшивой константностью.
Тип на
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 подытоживает это так:
Только чтение на английском языке, то здравый смысл соглашается с тем , что декларация должна быть одна декларация.
И не имеет значения, являются ли переменные указателями или нет. Объявление каждой переменной в одной строке делает код более понятным почти в каждом случае.
Так что спор о том, что программист запутался,
int* a, b
плохой. Корень проблемы - использование нескольких деклараторов, а не размещение*
. Независимо от стиля, вы должны написать это:Другим здравым, но субъективным аргументом было бы то, что данный
int* a
тип неa
вызывает сомнений,int*
и поэтому звезда относится к определителю типа.Но в основном мой вывод заключается в том, что многие из приведенных здесь аргументов являются лишь субъективными и наивными. Вы не можете в действительности дать веские аргументы для любого стиля - это действительно вопрос субъективных личных предпочтений.
1) CERT-C DCL04-C .
источник
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
Как великий пророк Дэн Сакс учит:« Если вы всегда располагаетеconst
как можно дальше вправо, не меняя смысловой смысл », это полностью прекращается. быть проблемой. Это также делает вашиconst
объявления более последовательными для чтения. «Все справа от слова const - это то, что является const, все слева - это его тип». Это будет применяться ко всем константам в декларации. Попробуйте это сint const * * const x;
Рассматривать
Это не переводит на
Это на самом деле переводится как
Это делает
int *x
обозначения несколько непоследовательными.источник
new
является оператором C ++ Его вопрос помечен как «C», а не «C ++».