В C и C ++ языков программирования, с оператором запятой ( в лице маркера ,
) представляет собой бинарный оператор , который оценивает свой первый операнд и отбрасывает результат, а затем оценивает второй операнд и возвращает это значение (и типа); между этими оценками есть точка последовательности .
Использование токена запятой в качестве оператора отличается от его использования в вызовах и определениях функций , объявлениях переменных, объявлениях перечислений и подобных конструкциях, где он действует как разделитель .
Синтаксис
Оператор запятой разделяет выражения (которые имеют значение) аналогично тому, как точка с запятой завершает операторы, а последовательности выражений заключаются в круглые скобки аналогично тому, как последовательности операторов заключаются в фигурные скобки: [1] (a, b, c)
- это последовательность выражений, разделенных запятыми, которое оценивается до последнего выражения, в c
то время как {a; b; c;}
является последовательностью операторов и не возвращает никакого значения. Запятая может находиться только между двумя выражениями - запятые отдельные выражения - в отличие от точки с запятой, которая стоит в конце (неблочного) оператора - точки с запятой завершают операторы.
Оператор-запятая имеет самый низкий приоритет среди всех операторов C и действует как точка последовательности . В сочетании запятых и точек с запятой, точки с запятой имеют более низкий приоритет, чем запятые, поскольку точки с запятой разделяют операторы, но запятые встречаются внутри операторов, что соответствует их использованию в качестве обычной пунктуации: a, b; c, d
группируется как, (a, b); (c, d)
потому что это два отдельных оператора.
Примеры
В этом примере различное поведение между второй и третьей строками связано с тем, что оператор запятой имеет более низкий приоритет, чем присваивание. Последний пример также отличается, поскольку возвращаемое выражение должно быть полностью вычислено, прежде чем функция сможет вернуться.
/ ** * Запятые действуют в этой строке как разделители, а не как оператор. * Результаты: a = 1, b = 2, c = 3, i = 0 * / int a = 1 , b = 2 , c = 3 , i = 0 ;/ ** * Присваивает значение b i. * Запятые действуют как разделители в первой строке и как оператор во второй строке. * Результаты: a = 1, b = 2, c = 3, i = 2 * / int a = 1 , b = 2 , c = 3 ; int i = ( a , b ); / ** * Присваивает значение переменной i. * Эквивалентно: int i = a; int b; * Запятые действуют как разделители в обеих строках. * Фигурные скобки во второй строке позволяют избежать повторного объявления переменной в том же блоке, * что может вызвать ошибку компиляции. * Второму объявленному b не присвоено начальное значение. * Результаты: a = 1, b = 2, c = 3, i = 1 * / int a = 1 , b = 2 , c = 3 ; { int i = a , b ; }/ ** * Увеличивает значение a на 2, затем присваивает значение результирующей операции a + b i. * Запятые действуют как разделители в первой строке и как оператор во второй строке. * Результаты: a = 3, b = 2, c = 3, i = 5 * / int a = 1 , b = 2 , c = 3 ; int i = ( a + = 2 , a + b ); / ** * Увеличивает значение a на 2, затем сохраняет значение a в i и отбрасывает неиспользуемые * значения результирующей операции a + b. * Эквивалентно: ((i = a) + = 2), a + b; * Запятые действуют как разделители в первой строке и как оператор в третьей строке. * Результаты: a = 3, b = 2, c = 3, i = 3 * / int a = 1 , b = 2 , c = 3 ; int i ; i = a + = 2 , a + b ;/ ** * Присваивает значение переменной i. * Запятые действуют как разделители в обеих строках. * Фигурные скобки во второй строке позволяют избежать повторного объявления переменной в том же блоке, * что может вызвать ошибку компиляции. * Вторые заявленные b и c не имеют начального значения. * Результаты: a = 1, b = 2, c = 3, i = 1 * / int a = 1 , b = 2 , c = 3 ; { int i = a , b , c ; }/ ** * Запятые действуют как разделители в первой строке и как оператор во второй строке. * Присваивает значение c переменной i, отбрасывая неиспользуемые значения a и b. * Результаты: a = 1, b = 2, c = 3, i = 3 * / int a = 1 , b = 2 , c = 3 ; int i = ( a , b , c );/ ** * Возвращает 6, а не 4, поскольку точки последовательности оператора запятой, следующие за ключевым словом * return, считаются одним выражением, оцениваемым как rvalue заключительного * подвыражения c = 6. * Запятые в этой строке действуют как операторы. * / return a = 4 , b = 5 , c = 6 ;/ ** * Возвращает 3, а не 1, по той же причине, что и в предыдущем примере. * Запятые в этой строке действуют как операторы. * / возврат 1 , 2 , 3 ;/ ** * Возвращает 3, а не 1, по той же причине, что и выше. Этот пример работает так же *, потому что return - это ключевое слово, а не вызов функции. Несмотря на то, что компиляторы * допускают конструкцию return (value), круглые скобки относятся только к «значению» * и не имеют особого влияния на ключевое слово return. * Return просто получает выражение, а здесь выражение - «(1), 2, 3». * Запятые в этой строке действуют как операторы. * / return ( 1 ), 2 , 3 ;
Использует
Варианты использования оператора запятой относительно ограничены. Поскольку он отбрасывает свой первый операнд, он обычно полезен только тогда, когда первый операнд имеет желательные побочные эффекты, которые должны быть упорядочены перед вторым операндом. Кроме того, поскольку он редко используется за пределами определенных идиом и его легко спутать с другими запятыми или точкой с запятой, он потенциально сбивает с толку и подвержен ошибкам. Тем не менее, есть определенные обстоятельства, при которых он обычно используется, особенно в циклах for и в SFINAE . [2] Для встроенных систем, которые могут иметь ограниченные возможности отладки, оператор запятой может использоваться в сочетании с макросом, чтобы легко переопределить вызов функции, чтобы вставить код непосредственно перед вызовом функции.
Для петель
Чаще всего используется для разрешения нескольких операторов присваивания без использования оператора блока, в первую очередь в выражениях инициализации и приращения цикла for . Это единственное идиоматическое использование в элементарном программировании на C. В следующем примере важен порядок инициализаторов цикла:
void rev ( char * s , size_t len ) { char * first ; for ( first = s , s + = len ; s > = first ; - s ) { putchar ( * s ); } }
Альтернативным решением этой проблемы на других языках является параллельное присваивание , которое позволяет выполнять несколько присваиваний в одном операторе, а также использует запятую, хотя и с другим синтаксисом и семантикой. Это используется в Go в аналогичном цикле for. [3]
Вне инициализаторов цикла for (которые имеют специальное использование точек с запятой) вместо точки с запятой может использоваться запятая, особенно когда рассматриваемые операторы работают аналогично приращению цикла (например, в конце цикла while):
++ p , ++ q ; ++ p ; ++ q ;
Макросы
Запятую можно использовать в макросах препроцессора для выполнения нескольких операций в пространстве одного синтаксического выражения.
Одним из распространенных способов использования является предоставление настраиваемых сообщений об ошибках в неудачных утверждениях. Это делается путем передачи в assert
макрос списка выражений в скобках , где первое выражение является строкой ошибки, а второе выражение - утвержденным условием. assert
Макро выдает свой аргумент дословно на неудачу утверждение. Ниже приведен пример:
#include #include int main ( void ) { int я ; for ( я = 0 ; я <= 9 ; я ++ ) { assert ( ( "я слишком большой!" , я <= 4 ) ); printf ( "я =% я \ п " , я ); } return 0 ; }
Выход:
я = 0я = 1я = 2я = 3я = 4assert: assert.c: 6: test_assert: Assertion `(" i is too big! ", i <= 4) 'не удалось.Прервано
Однако макрос assert обычно отключен в производственном коде, поэтому используйте его только для целей отладки.
Условие
Запятую можно использовать в условии (if, while, do while или for), чтобы разрешить вспомогательные вычисления, в частности вызов функции и использование результата, с областью видимости блока :
if ( y = f ( x ), y > x ) { ... // операторы, включающие x и y }
Похожая идиома существует в Go , где синтаксис оператора if явно допускает необязательный оператор. [4]
Комплексный возврат
Запятую можно использовать в операторах возврата, чтобы присвоить глобальной переменной или параметру out (передаваемому по ссылке). Эта идиома предполагает, что присваивания являются частью возврата, а не вспомогательными присваиваниями в блоке, который заканчивается фактическим возвратом. Например, при установке глобального номера ошибки:
если ( сбой ) return ( errno = EINVAL , -1 );
Более подробно это можно записать как:
если ( сбой ) { errno = EINVAL ; возврат -1 ; }
Избегайте блокировки
Для краткости можно использовать запятую, чтобы избежать блока и связанных фигурных скобок, например:
если ( x == 1 ) y = 2 , z = 3 ;
если ( x == 1 ) y = 2 , z = 3 ;
вместо:
если ( х == 1 ) { у = 2 ; z = 3 ;}
если ( х == 1 ) { у = 2 ; z = 3 ; }
Другие языки
В языках программирования OCaml и Ruby для этой цели используется точка с запятой («;»). В JavaScript [5] и Perl [6] оператор запятой используется так же, как в C / C ++. В Java запятая - это разделитель, используемый для разделения элементов в списке в различных контекстах. [7] Это не оператор и не оценивает последний элемент в списке. [8]
Смотрите также
Рекомендации
- ^ "Оператор запятой:," . Документы Microsoft для разработчиков . Архивировано из оригинала (html) 1 августа 2019 года . Проверено 1 августа 2019 .
Два выражения, разделенные запятой, оцениваются слева направо.
Левый операнд всегда оценивается, и все побочные эффекты завершаются до того, как оценивается правый операнд.
- ^ http://en.cppreference.com/w/cpp/language/sfinae
- ^ Эффективный Go : для «Наконец, в Go нет оператора запятой, а ++ и - являются операторами, а не выражениями. Таким образом, если вы хотите запустить несколько переменных в for, вам следует использовать параллельное присвоение (хотя это исключает ++ и - ). "
- ^ Спецификация языка программирования Go: операторы If
- ^ «Оператор-запятая» . Веб-документы MDN . 17 января 2020. Архивировано из оригинала (HTML) 12 июля 2014 года . Проверено 25 января 2020 года .
Вы можете использовать оператор запятой, если хотите включить несколько выражений в место, для которого требуется одно выражение.
- ^ https://perldoc.perl.org/perlop.html#Comma-Operator
- ^ «2.4. Грамматические обозначения» . Корпорация Oracle . Архивировано из оригинала (html) 22 июля 2019 года . Проверено 25 июля 2019 .
- ^ https://stackoverflow.com/questions/38733508/is-comma-operator-or-separator-in-java
Библиография
- Рамаджаран В. (1994), Компьютерное программирование на языке Си , Нью-Дели: Prentice Hall of India
- Диксит, JB (2005), Основы компьютеров и программирования на C , Нью-Дели: Laxmi Publications
- Керниган, Брайан В .; Ричи, Деннис М. (1988), Язык программирования C (2-е изд.), Englewood Cliffs, NJ: Prentice Hall
Внешние ссылки
- « Эффект от использования запятой вместо точки с запятой в C и C ++ », Stack Overflow
- Boost.Assignment - средство из библиотеки Boost, которое упрощает заполнение контейнеров путем перегрузки оператора запятой