хороший способ написать «указатель на что-то» в C / C ++ - PullRequest
3 голосов
/ 16 февраля 2011

Есть ли "хороший" способ написать "указатель на что-то" в C / C ++?
Я использую для записи void foo( char *str ); Но иногда я нахожу это довольно нелогичным, потому что тип str is "указатель наchar ", тогда логичнее было бы присоединить * к имени типа.
Есть ли правило для написания указателей?

char*str;
char* str;
char *str;
char * str;

Ответы [ 5 ]

22 голосов
/ 16 февраля 2011

Не существует строгого правила, но имейте в виду, что * присоединяется к переменной, поэтому:

char *str1, *str2; // str1 and str2 are pointers
char* str1, str2;  // str1 is a pointer, str2 is a char

Некоторые люди тоже любят делать char * str1, но это зависит от вас или от стандарта вашей компании.

9 голосов
/ 16 февраля 2011

Общепринятым соглашением C является запись T *p, тогда как общепринятым соглашением C ++ является запись T* p. Оба разбираются как T (*p); * является частью объявления, а не спецификатором типа. Это просто случайность синтаксиса объявления указателя, который вы можете написать любым способом.

Синтаксис объявления C (и, соответственно, C ++): выражение-центрированное ; Таким образом, форма объявления должна соответствовать форме выражения того же типа в коде.

Например, предположим, что у нас был указатель на int, и мы хотели получить доступ к этому целочисленному значению. Для этого мы разыменовываем указатель с помощью оператора косвенного обращения *, например:

x = *p; 

Тип выражения *p равен int; таким образом, из этого следует, что объявление p должно быть

int *p  

Внутренность p предоставляется спецификатором типа int, а указатель p предоставляется декларатором *p.

В качестве немного более сложного примера, предположим, что у нас был указатель на массив float, и мы хотели получить доступ к значению с плавающей запятой в i -ом элементе массива через указатель. Мы разыменовываем указатель массива и добавляем результат:

f = (*ap)[i];

Тип выражения (*ap)[i] равен float, поэтому из объявления указателя массива следует

float (*ap)[N];

Число с плавающей запятой ap предоставляется спецификатором типа float, а указатель и массив - с помощью декларатора (*ap)[N]. Обратите внимание, что в этом случае * должен явно быть привязанным к идентификатору; [] имеет более высокий приоритет, чем унарный * в синтаксисе выражений и объявлений, поэтому float* ap[N] будет анализироваться как float *(ap[N]), или "массив указателей на float", а не "указатель на массив float». Я полагаю, вы могли бы написать это как

float(* ap)[N];

но я не уверен, какой смысл; это не делает тип ap более понятным.

Еще лучше, как насчет указателя на функцию, которая возвращает указатель на массив указателей на int:

int *(*(*f)())[N];

Опять же, по крайней мере два из * оператора должны быть явно связаны в объявителе; привязка последнего * к спецификатору типа, как в

int* (*(*f)())[N];

просто указывает на запутанное мышление ИМО.

Несмотря на то, что я использую его в своем собственном коде C ++, и хотя я понимаю, почему он стал популярным, у меня есть проблема с обоснованием соглашения T* p, заключающееся в том, что оно просто не применяется вне простейших объявлений указателей, и это усиливает упрощенное представление о том, что точка неправильного синтаксиса объявлений C и C ++. Да, тип p - это «указатель на T», но это не меняет того факта, что с точки зрения грамматики языка * привязывается к декларатору, а не к спецификатору типа.

В другом случае, если тип a равен «N-элементному массиву T», мы не пишем

T[N] a;

Очевидно, грамматика не позволяет этого. Опять же, аргумент просто не применяется в этом случае.

EDIT

Как отметил Стив в комментариях, вы можете использовать typedefs, чтобы скрыть некоторые сложности. Например, вы можете переписать

int *(*(*f)())[N];

как что-то вроде

typedef int *iptrarr[N];           // iptrarr is an array of pointer to int
typedef iptrarr *arrptrfunc();     // arrptrfunc is a function returning
                                   // a pointer to iptrarr

arrptrfunc *f;                     // f is a pointer to arrptrfunc

Теперь вы можете безоговорочно применить соглашение T* p, объявив f как arrptrfunc* f. Лично мне не нравится делать вещи таким образом, так как из определения типа не обязательно ясно, как f предполагается использовать в выражении или как использовать объект типа arrptrfunc. Версия без определения типа может быть уродливой и трудной для чтения, но, по крайней мере, она говорит вам все, что вам нужно знать заранее; Вам не нужно копаться во всех typedef.

3 голосов
/ 16 февраля 2011

«Хороший путь» зависит от

  1. внутренних стандартов кодирования в вашем проекте
  2. ваших личных предпочтений

(вероятно) в этом порядке

1 голос
/ 16 февраля 2011

В этом нет ничего правильного или неправильного. Важно выбрать один стандарт кодирования и придерживаться его.

При этом я лично считаю, что * принадлежит типу, а не имени переменной, поскольку типом является "указатель на символ". Имя переменной не является указателем.

0 голосов
/ 16 февраля 2011

Я думаю, что это будет в значительной степени зависеть от общей схемы объявления переменных.

Например, у меня есть тенденция объявлять только одну переменную в строке.Таким образом, я могу добавить комментарий, напоминающий мне, как использовать переменную.

Однако бывают случаи, когда целесообразно объявить несколько переменных одного типа в одной строке.При таких обстоятельствах мое личное правило кодирования - никогда, НИКОГДА, НИКОГДА не объявлять указатели на одной строке с не указателями.Я считаю, что их смешивание может быть источником ошибок, поэтому я стараюсь упростить обнаружение «неправильности», избегая смешивания.

Пока я следую первому руководству, я считаю, что это не имеет значенияслишком много, как я объявляю указатели , пока я последовательный .

Однако, если я использую второе указание и объявляю несколько указателей в одной строке, я нахожу следующий стиль:наиболее полезный и понятный (конечно, другие могут не согласиться) ...

char  *ptr1, *ptr2, *ptr3;

Поскольку между * и именем указателя нет пробела, становится легче определить, нарушил ли я второе правило.

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

char  *ptr;

В любом случае, это мое обоснование для частипочему я делаю то, что делаю.Надеюсь, это поможет.

...