Существует ли Соглашение для объявлений указателей в C? [закрытый]
Когда объявляет указатели в C, есть 2 (edit: 3) варианта:
Вариант А:int* ptr;
Вариант В:int *ptr;
Вариант С:int * ptr;
- в A оператор косвенного обращения был добавлен к типу.
- в B оператор косвенности был добавлен к переменной.
- в языке C оператор косвенности свободно располагается между типом и переменной.
Способ объявления указателя отличается в зависимости от тип документации, которую я читал. Некоторые авторы, по-видимому, отдают предпочтение определенным вариантам, другие используют несколько.
- Правильно ли я предполагаю, что нет никакой разницы в функциональности между различными вариантами?
- Если да, то существует ли Соглашение, для которого вариант следует использовать В С?
8 ответов:
Нет абсолютно никакой разницы в функциональности между
int* ptr;И
int *ptr;Что вы используете, зависит от вас, есть несколько конфликтующих стилей кодирования на выбор.
То, о чем никто больше не упоминал, - это
int *ptr;Более точно соответствует грамматике языка.
int *ptr;- это объявление , которое состоит из:
- A объявление-спецификатор
int, Затем следует- A Декларатор,
*ptr.(это на самом деле пропускает несколько шагов, но это дает основную идею.)
Поскольку объявление следует за использованием, это означает, что
Можно было бы возразить, что это делает его лучше, чем*ptrявляется типаint. Из этого следует, чтоptrимеет видint*.int* ptr;По той же причине, что
x = y+z;Лучше, чем
x=y + z;Конечно, вы можете написать
int* ptr;И читать его как "
Но какой бы интервал вы ни выбрали, вам нужно понять, что на самом деле означаетptrимеет типint*". И многие программисты делают именно это, и прекрасно ладят (это, как правило, предпочтительный стиль в C++). Компилятору все равно, каким способом вы это делаете, да и кому угодно чтение вашего кода не должно иметь проблем с его пониманием в любом случае.int *ptr;, чтобы, когда вы увидитеint *ptr, i;В чужом коде (что неизбежно произойдет) вы сразу поймете, что
ptr- это указатель, аi- int.И если вы работаете с другими программистами над проектом, вы должны следовать всем существующим соглашениям в стандартах кодирования, или если их нет во-первых, то, как код уже написан. Лично я предпочитаю
int *ptr;int* ptr;, но использование смеси обоих стилей намного хуже, чем последовательное использование любого из них.
Это имеет значение только тогда, когда вы планируете объявить несколько переменных одного и того же типа в одной строке. Например, если вам нужно несколько указателей int, вам нужно сделать следующее:
int *a, *b, *c;Стилистически, однако, это сбивает с толку, когда вы объявляете только одну переменную. Многим людям нравится видеть тип, за которым следует имя переменной, и тип должен быть указателем на int, а не int, поэтому они предпочитают:
Это в конечном счете зависит от вас, предпочитаете ли вы одну форму другой. За 20 лет профессионального программирования на языке Си я видел, как около 50% людей выбирают один из них.int* a; int* b; int* c;
Они оба означают то же самое, что и другие. Однако тебя ждет ловушка. Рассмотрим этот код:
int* a, b;Вы можете подумать, что это объявлено указателям на
int. Не так, а иначе. На самом делеaестьint*, ноbестьint. Это одна из причин, по которой многие программисты на языке Си предпочитают ставить*рядом с переменной, а не с типом. Когда написано вот так:int *a, b;Вы с меньшей вероятностью будете введены в заблуждение относительно того, что
aиbявляются.Сказав Все это, многие стандарты кодирования настаивают на том, что вы объявляете не более одной переменной в строке, т. е.]}
int* a; int b;Если вы следуете правилу "одна переменная на строку", то здесь определенно нет места для путаницы.
T *a;- это предпочтительный способ объявления указателя в стиле C на
T, используемый в книге Кернигана и Ричи о C.T* a;Является предпочтительным в стиле C++ путь к объявить указатель на
T, используемый в книге Страуструпа ПРО С++.Оба обозначения эквивалентны.
Объявления
C основаны на типахвыражений , а не на объектах.
Если у вас есть указатель на
intс именемpi, и вы хотите получить доступ к целочисленному значению, на которое он указывает, вы должны разыменовать указатель, как в:x = *pi; printf("%d", *pi); *pi = 1 + 2;И т. д. Тип выражения
*piisint: следовательно, декларация должна читаться какТеперь предположим, что у вас есть массив указателей наint *pi;char; чтобы добраться до любого символа, вам нужно сначала подстрочный индекс в массив, затем разыменование результата:c = *pc[i]; if (*pc[j] == 'a') {...}И т. д. Опять же, тип выражения
*pc[i]являетсяchar, поэтому декларация читается какchar *pc[N];И
*pi, и*pc[N]известны какдеклараторы и задают дополнительную информацию о типе, не заданную спецификатором типа. IOW, массив-ness и указатель-nesspcзадаются как часть Декларатора, в то время какchar-ness задается спецификатором типа.Что касается вопрос о том, что такое стиль, являетсяправильным ...
Ни один из них не является" правильным", но я (и многие другие программисты C) предпочитаю писать
T *pв отличие отT* p, поскольку он более точно отражает грамматику языка (*является частью Декларатора) и помогает избежать путаницы при объявлении нескольких элементов. Я видел слишком много примеров людей, пишущихT* a, b;и ожидающих, чтоbбудет указателем.Обычный ответ на эту критику - "не делай этого". объявляйте более одного элемента в строке."Мой ответ на этот ответ:" напишите ваши деклараторы правильно, и у вас не будет никаких проблем".
Существует другая школа мышления среди многих программистов на C++, которые предпочитают стильT* p, и я должен сказать, что есть несколько случаев (ограничиваясь C++), когда он может сделать код более читаемым. Однако это работает только для простых указателей наT: парадигма быстро ломается, когда вы начинаете работать с массивами указателей, или указателей на массивы, или указателей на функции, или указателей на массивы указателей на функции и т. д. Я имею в виду, написать что-то вродеT* (*(*p)[N])(); // p is a pointer to an array of pointers to functions // returning pointers to T.Просто указывает на запутанное мышление. Хотя, если вы действительно действительно чувствуете, что вы должны следовать парадигме
T* p, вы всегда можете создать ряд типов:править
Попробуем еще раз:
Ради всего святого, не делай этого.typedef T* TPtr; // pointer to T typedef TPtr TPtrFunc(); // function returning pointer to T typedef TPtrFunc* TPtrFuncPtr; // pointer to function returning // pointer to T typedef TPtrFuncPtr TPtrFuncPtrArray[N]; // N-element array of pointer to function // returning pointer to T TPtrFuncPtrArray* p;
В языке C пробелы не имеют значения, за исключением тех случаев, когда они необходимы для разделения маркеров. Оба варианта синтаксически верны.
Вариант 1 связывает оператор указателя с типом, что достаточно логично. Для меня этого было бы достаточно, если бы не было причины, по которой Вариант 2 имеет смысл.
Вариант 2 согласуется с тем, как структурированы объявления C. В грамматике языка Си оператор указателя относится к декларатору (то есть к имени), а не к типу. Это имеет значение при объявлении нескольких переменных в одном объявлении. Есть также ряд более эзотерических случаев, когда это имеет значение.
Таким образом, для меня Вариант 2 более согласуется с С. Но любой вариант оправдан, и оба варианта условны.
Вы правы, оба означают для компилятора одно и то же. Оба оператора будут производить переменную типа
(int *).Что касается того, что правильно: банка червей! Обычно это тема для дебатов. Если вы работаете в компании или на OSS, вероятно, лучше всего придерживаться определенного стиля кодирования. Если его нет, я обычно использую стиль LNT (не оставляю следов), соответствующий любому стилю, который явно использовался в этой части базы кода.
Можно утверждать, что к читатель один легче понять. Например,
int* ptr;помещает*ближе кint, что более четко связывает мы говорим о типе(int *)...