Подтвердить что ты не робот

Синтаксис указателя в C: почему * применяется только к первой переменной?

Следующее объявление в C:

int* a, b;

объявит a как тип int* и b как тип int. Я хорошо знаю эту ловушку, но я хочу знать, почему она работает именно так. Почему он также не объявляет b как int*, как это ожидало бы большинство людей? Другими словами, почему * применяется к имени переменной, а не к типу?

Конечно, вы могли бы написать это так, чтобы быть более совместимым с тем, как это работает:

int *a, b;

Тем не менее, я и все, с кем я говорил, с точки зрения типа "указатель на int", а не a, являются указателем на некоторые данные, а тип данных - "int".

Было ли это просто плохое решение дизайнеров C, или есть какая-то веская причина, почему он разбирается таким образом? Я уверен, что на этот вопрос был дан ответ, но я не могу найти его, используя поиск.

4b9b3361

Ответ 1

Здесь есть веб-страница "Развитие языка C" , в которой говорится: "Синтаксис этих объявлений отражает наблюдение, что i, * pi и ** ppi, все дают тип int при использовании в выражении." Найдите это предложение на странице, чтобы найти соответствующий раздел, в котором говорится об этом вопросе.

Ответ 2

C декларации были написаны таким образом, чтобы использовать "зеркала объявлений". Вот почему вы объявляете массивы следующим образом:

int a[10];

Было ли у вас правило, которое вы предлагаете, где оно всегда

type identifier, identifier, identifier, ... ;

... тогда массивы логически должны быть объявлены следующим образом:

int[10] a;

что прекрасно, но не отражает, как вы используете a. Обратите внимание, что это тоже относится к функциям - мы объявляем такие функции:

void foo(int a, char *b);

а не

void(int a, char* b) foo;

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


После некоторого дальнейшего размышления, я думаю, также стоит отметить, что орфографический "указатель на int" как "int*" является следствием использования зеркал использования объявлений в любом случае. Если вы собираетесь использовать другой стиль объявления, вероятно, было бы разумнее записать "указатель на int" как "&int" или что-то совершенно другое, например "@int".

Ответ 3

Возможно, существует дополнительная историческая причина, но я всегда понимал ее так:

Одно объявление, один тип.

Если a, b, c и d должны быть одного типа:

int a, b, c, d;

Тогда все в строке должно быть целым.

int a, *b, **c, ***d;

4 целых числа:

  • а
  • * Ь​​li >
  • ** с
  • *** д

Это может быть связано и с приоритетом оператора, или, возможно, это было в какой-то момент в прошлом.

Ответ 4

* изменяет имя переменной, а не спецификатор типа. Это в основном из-за того, как анализируется *. Возьмите эти утверждения:

char*  x;
char  *x;

Эти утверждения эквивалентны. Оператор * должен находиться между спецификатором типа и именем переменной (он рассматривается как оператор инфикса), но он может идти по обеим сторонам пространства. Учитывая это, декларация

int*  a, b;

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

Также подумайте об этом так: когда вы пишете объявление int x;, вы указываете, что x является целым числом. Если y является указателем на целое число, то *y является целым числом. Когда вы пишете int *y;, вы указываете, что *y является целым числом (которое вы хотите). В заявлении char a, *b, ***c; вы указываете, что переменная a, разыменованное значение b и трехкратно разыменованное значение c имеют тип char. Объявление переменных таким образом делает использование оператора звезды (почти) согласующимся с разыменованием.

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

Ответ 5

Я предполагаю, что это связано с полным синтаксисом объявления для модификаторов типов:

int x[20], y;
int (*fp)(), z;

В этих примерах гораздо более очевидно, что модификаторы влияют только на одно из объявлений. Предполагается, что однажды K & R решил разработать модификаторы таким образом, он чувствовал себя "правильным", чтобы модификаторы влияли только на одно объявление.

С другой стороны, я бы рекомендовал ограничиться только одной переменной для объявления:

int *x;
int y;

Ответ 6

Потому что если утверждение

int* a, b;

должны были объявить b как указатель, тогда у вас не было бы способа объявить

int* a;
int  b;

на одной строке.

С другой стороны, вы можете сделать

int*a, *b;

чтобы получить то, что вы хотите.

Подумайте об этом так: так, как сейчас, это все еще самый сжатый и пока единственный способ сделать это. Это то, что C в основном о:)

Ответ 7

Рассмотрим объявление:

int *a[10];
int (*b)[10];

Первый представляет собой массив из десяти указателей на целые числа, второй - указатель на массив из десяти целых чисел.

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

Ответ 8

Я могу только догадываться, почему нужно было бы повторить * для каждого имени переменной, упомянутого в строке, чтобы сделать их все указатели. Возможно, это связано с решением о согласованности языка? Позвольте мне проиллюстрировать это на примере:


Предположим, что у вас есть функция foo, объявленная следующим образом:

int foo() { ... }

Предположим также, что вы хотите объявить два указателя на эту функцию:

int (*fooptr1, fooptr2)();
// this doesn't work; and even if it did, what would the syntax possibly
// look like to initializing them to function foo?
// int (*fooptr1 = foo, fooptr2 = foo)() ?

int (*fooptr1)() = foo, (*fooptr2)() = foo;
// this works.

В этом случае вам просто нужно повторить объявление всего типа для обеих переменных, и вы не ошибетесь, потому что нет другого способа сделать это (учитывая синтаксис объявления C как таковой).


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

(Не забывайте, что я просто угадываю здесь.)