Как компилятор знает, что запятая в вызове функции не является оператором запятой?
Рассмотрим вызов функции (вызов 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.