Существует ли Соглашение для объявлений указателей в 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;
И т. д. Тип выражения
*pi
isint
: следовательно, декларация должна читаться какТеперь предположим, что у вас есть массив указателей на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 *)
...