Существует ли Соглашение для объявлений указателей в C? [закрытый]


Когда объявляет указатели в C, есть 2 (edit: 3) варианта:

Вариант А:
int* ptr;

Вариант В:
int *ptr;

Вариант С:
int * ptr;

  • в A оператор косвенного обращения был добавлен к типу.
  • в B оператор косвенности был добавлен к переменной.
  • в языке C оператор косвенности свободно располагается между типом и переменной.

Способ объявления указателя отличается в зависимости от тип документации, которую я читал. Некоторые авторы, по-видимому, отдают предпочтение определенным вариантам, другие используют несколько.

    Правильно ли я предполагаю, что нет никакой разницы в функциональности между различными вариантами?
  • Если да, то существует ли Соглашение, для которого вариант следует использовать В С?
8 11

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, поэтому они предпочитают:

int* a;
int* b;
int* c;
Это в конечном счете зависит от вас, предпочитаете ли вы одну форму другой. За 20 лет профессионального программирования на языке Си я видел, как около 50% людей выбирают один из них.

Они оба означают то же самое, что и другие. Однако тебя ждет ловушка. Рассмотрим этот код:

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 is int: следовательно, декларация должна читаться как

int *pi;
Теперь предположим, что у вас есть массив указателей на char; чтобы добраться до любого символа, вам нужно сначала подстрочный индекс в массив, затем разыменование результата:
c = *pc[i];
if (*pc[j] == 'a') {...}

И т. д. Опять же, тип выражения *pc[i] является char, поэтому декларация читается как

char *pc[N];

И *pi, и *pc[N] известны какдеклараторы и задают дополнительную информацию о типе, не заданную спецификатором типа. IOW, массив-ness и указатель-ness pc задаются как часть Декларатора, в то время как 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 *)...