Как компилятор знает, что запятая в вызове функции не является оператором запятой?

Рассмотрим вызов функции (вызов int sum(int, int))

printf("%d", sum(a,b));

Как компилятор решил, что ,, используемый в вызове функции sum(int, int), не является оператором запятой?

ПРИМЕЧАНИЕ. Я не хотел использовать оператор запятой в вызове функции. Я просто хотел знать, как компилятор знает, что он не является оператором запятой.

Ответы

Ответ 1

Посмотрите на грамматику для языка C. Он полностью внесен в Приложение A стандарта . Способ, которым он работает, состоит в том, что вы можете выполнить каждый токен в программе на языке C и сопоставить их со следующим элементом в грамматике. На каждом шаге у вас есть только ограниченное количество опций, поэтому интерпретация любого заданного символа будет зависеть от контекста, в котором он появляется. Внутри каждого правила грамматики каждая строка дает действительную альтернативу для соответствия программе.

В частности, если вы ищете parameter-list, вы увидите, что он содержит явную запятую. Поэтому, когда компилятор C-парсер находится в режиме "список параметров", запятые, которые он находит, будут пониматься как разделители параметров, а не как операторы запятой. То же самое верно для скобок (которые также могут встречаться в выражениях).

Это работает, потому что правило parameter-list осторожно использует правила assignment-expression, а не просто правило expression. expression может содержать запятые, тогда как assignment-expression не может. Если бы это было не так, то грамматика была бы неоднозначной, и компилятор не знал бы, что делать, когда он столкнулся с запятой внутри списка параметров.

Однако открывающая скобка, например, не являющаяся частью определения функции/вызова, или оператора if, while или for, будет интерпретироваться как часть выражения (потому что там другой вариант, но только если начало выражения является допустимым выбором в этой точке), а затем в скобках будут применяться синтаксические правила expression, которые позволят использовать операторы запятой.

Ответ 2

Из C99 6.5.17:

Как указано в синтаксисе, оператор запятой (как описано в этом подпункте) не может отображаются в контекстах, где запятая используется для разделения элементов в списке (например, аргументы для функций или списков инициализаторов). С другой стороны, его можно использовать в выражении в скобках или во втором выражение условного оператора в таких контекстах. В вызове функции

f(a, (t=3, t+2), c)

функция имеет три аргумента, вторая из которых имеет значение 5.

Другим подобным примером является список инициализаций массивов или структур:

int array[5] = {1, 2};
struct Foo bar = {1, 2};

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

sum((a,b))

Это не будет компилироваться, конечно.

Ответ 3

Причина - грамматика C. В то время как все остальные, похоже, любят приводить пример, реальная сделка - это грамматика структуры фразы для вызовов функций в стандарте (C99). Да, вызов функции состоит из оператора (), примененного к постфиксному выражению (например, идентификатор):

 6.5.2 postfix-expression:
       ...
       postfix-expression ( argument-expression-list_opt )

вместе с

argument-expression-list:
       assignment-expression
       argument-expression-list , assignment-expression    <-- arglist comma

expression:
       assignment-expression
       expression , assignment-expression                  <-- comma operator

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

Ответ 4

Существующие ответы говорят "потому что спецификация языка C говорит, что это разделитель списков, а не оператор".

Однако ваш вопрос задает вопрос "как компилятор знает...", и что совсем другое: он действительно ничем не отличается от того, как компилятор знает, что запятая в printf("Hello, world\n"); не является оператором запятой: компилятор "знает" из-за контекста, где появляется запятая - в основном, что было раньше.

Язык C 'можно описать в Бэксу-Наур Форма (BNF) - по существу, набор правил, которые компилятор parser использует для сканирования вашего входного файла. BNF для C будет различать эти различные возможные вхождения запятых в язык.

Есть много хороших ресурсов, как работают компиляторы, и как написать один.

Ответ 5

Проект стандарта C99 гласит:

Как указано в синтаксисе, оператор запятой (как описано в этом подпункте) не может отображаются в контекстах, где запятая используется для разделения элементов в списке (например, аргументы для функций или списки инициализаторов). С другой стороны, его можно использовать в выражении в скобках или во втором выражении условного оператора в таких контекстах. В вызове функции f(a, (t=3, t+2), c) функция имеет три аргумента, вторая из которых имеет значение 5.

Другими словами, "потому что".

Ответ 6

В этом вопросе есть несколько аспектов. Один из них заключается в том, что в определении так сказано. Ну, как компилятор знает, в каком контексте эта запятая? Это работа парсера. Для C, в частности, язык может быть проанализирован парсером LR (1) (http://en.wikipedia.org/wiki/Canonical_LR_parser).

Как это работает, парсер генерирует кучу таблиц, которые составляют возможные состояния анализатора. В некоторых состояниях действует только определенный набор символов, и символы могут иметь разное значение в разных состояниях. Парсер знает, что он анализирует функцию из-за предыдущих символов. Таким образом, он знает, что возможные состояния не включают запятый оператор.

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