stringtranslate.com

Оператор запятая

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

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

Синтаксис

Оператор запятая разделяет выражения (имеющие значение) аналогично тому, как точка с запятой завершает операторы, а последовательности выражений заключаются в скобки аналогично тому, как последовательности операторов заключаются в фигурные скобки: [1] (a, b, c) — это последовательность выражений, разделенных запятыми, которая вычисляется до последнего выражения c, в то время как {a; b; c;}— это последовательность операторов, и она не вычисляется до какого-либо значения. Запятая может встречаться только между двумя выражениями — запятые разделяют выражения — в отличие от точки с запятой, которая стоит в конце (неблочного) оператора — точки с запятой завершают операторы.

Оператор запятая имеет самый низкий приоритет среди всех операторов C и действует как точка последовательности . В сочетании запятых и точек с запятой точки с запятой имеют более низкий приоритет, чем запятые, поскольку точки с запятой разделяют операторы, но запятые встречаются внутри операторов, что соответствует их использованию в качестве обычных знаков препинания: a, b; c, dгруппируется как , (a, b); (c, d)поскольку это два отдельных оператора.

Оператор запятая устарел в выражениях с подстрочными индексами (начиная с C++20 ); [2] чтобы уменьшить путаницу и открыть будущую возможность перепрофилирования синтаксиса для индексации многомерных массивов. В C++23 была добавлена ​​возможность перегрузки operator[]с несколькими аргументами, что сделало выражения с запятой без скобок непригодными для использования в подстрочных индексах. [3] Оператор запятая по-прежнему применим и не устарел в этом контексте, если выражение с запятой заключено в скобки (как в a[(b,c)]).

Примеры

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

/** * Запятые в этой строке действуют как разделители, а не как оператор. * Результаты: 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 ); /** * Присваивает значение a переменной 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 ;                       /** * Присваивает значение a переменной 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, по той же причине, что и в предыдущем примере. * Запятые в этой строке выполняют роль операторов. */ return 1 , 2 , 3 ;   /** * Возвращает 3, а не 1, по той же причине, что и выше. Этот пример работает так, как он работает, * потому что return — это ключевое слово, а не вызов функции. Несмотря на то, что компиляторы * допускают конструкцию return(value), скобки относятся только к "value" * и не оказывают особого влияния на ключевое слово return. * Return просто получает выражение, и здесь выражение — "(1), 2, 3". * Запятые действуют как операторы в этой строке. */ return ( 1 ), 2 , 3 ;  

Использует

Оператор запятой имеет относительно ограниченное количество вариантов использования. Поскольку он отбрасывает свой первый операнд, он, как правило, полезен только там, где первый операнд имеет желаемые побочные эффекты , которые должны быть упорядочены перед вторым операндом. Кроме того, поскольку он редко используется вне определенных идиом и его легко спутать с другими запятыми или точкой с запятой, он потенциально сбивает с толку и подвержен ошибкам. Тем не менее, существуют определенные обстоятельства, когда он обычно используется, особенно в циклах for и в SFINAE . [4] Для встроенных систем, которые могут иметь ограниченные возможности отладки, оператор запятой может использоваться в сочетании с макросом для бесшовного переопределения вызова функции, чтобы вставить код непосредственно перед вызовом функции.

Для петель

Наиболее распространенное использование — разрешить множественные операторы присваивания без использования оператора блока, в первую очередь в выражениях инициализации и инкремента цикла for . Это единственное идиоматическое использование в элементарном программировании на языке C. В следующем примере порядок инициализаторов цикла имеет значение:

void rev ( char * s , size_t len ) { char * first ; for ( first = s , s += len ; s >= first ; -- s ) { putchar ( * s ); } }                    

Альтернативным решением этой проблемы в других языках является параллельное присваивание , которое позволяет выполнять множественные присваивания в одном операторе, а также использует запятую, хотя и с другим синтаксисом и семантикой. Это используется в Go в его аналогичном цикле for. [5]

За пределами инициализаторов цикла for (где точка с запятой используется особым образом) вместо нее можно использовать запятую, особенно когда рассматриваемые операторы функционируют аналогично инкременту цикла (например, в конце цикла while):

++ п , ++ д ; ++ п ; ++ д ;  

Макросы

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

Одним из распространенных вариантов использования является предоставление пользовательских сообщений об ошибках в неудачных утверждениях. Это делается путем передачи макросу списка выражений в assertскобках, где первое выражение — строка ошибки, а второе выражение — утверждаемое условие. Макрос assertвыводит свой аргумент дословно при неудачном утверждении. Ниже приведен пример:

#include <stdio.h> #include <assert.h>  int main ( void ) { int i ; for ( i = 0 ; i <= 9 ; i ++ ) { assert ( ( "i слишком большой!" , i <= 4 ) ); printf ( "i = %i \n " , i ); } return 0 ; }                        

Выход:

я = 0я = 1я = 2я = 3я = 4assert: assert.c:6: test_assert: Утверждение `( "i is too big!", i <= 4 )' не выполнено.Прервано

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

Состояние

Запятую можно использовать внутри условия (if, while, do while или for), чтобы разрешить вспомогательные вычисления, в частности, вызов функции и использование результата с блочной областью действия :

если ( y = f ( x ), y > x ) { ... // операторы, включающие x и y }         

Похожая идиома существует в Go , где синтаксис оператора if явно допускает необязательный оператор. [6]

Комплексный возврат

Запятая может использоваться в операторах return для назначения глобальной переменной или выходному параметру (передаваемому по ссылке). Эта идиома предполагает, что назначения являются частью return, а не вспомогательными назначениями в блоке, который завершается фактическим return. Например, при установке глобального номера ошибки:

если ( неудача ) вернуть ( errno = EINVAL , -1 );      

Более развернуто это можно записать так:

если ( неудача ) { errno = EINVAL ; return -1 ; }       

Избегайте блока

Для краткости можно использовать запятую, чтобы избежать блока и связанных с ним фигурных скобок, например:

если ( х == 1 ) у = 2 , z = 3 ;         
если ( х == 1 ) у = 2 , z = 3 ;         

вместо:

если ( х == 1 ) { у = 2 ; z = 3 ;}         
если ( х == 1 ) { у = 2 ; z = 3 ; }          

Другие языки

В языках программирования OCaml и Ruby для этой цели используется точка с запятой («;»). JavaScript [7] и Perl [8] используют оператор запятой таким же образом, как это делают C/C++. В Java запятая является разделителем, используемым для разделения элементов в списке в различных контекстах. [9] Это не оператор и не вычисляется до последнего элемента в списке. [10]

Смотрите также

Ссылки

  1. ^ "Оператор запятая". Microsoft dev docs . Архивировано из оригинала 2 августа 2019 г. Получено 1 августа 2019 г. Два выражения, разделенные запятой, вычисляются слева направо. Левый операнд всегда вычисляется, и все побочные эффекты завершаются до того, как вычисляется правый операнд.
  2. ^ "P1161R2: Отменить использование оператора запятой в выражениях с подписью". www.open-std.org . Получено 05.09.2022 .
  3. ^ Марк Хёммен; Дейзи Холлман; Корантен Жабо; Изабелла Муэрте; Кристиан Тротт (14 сентября 2021 г.). «Многомерный индексный оператор» (PDF) .
  4. ^ "SFINAE". en.cppreference.com . Получено 2022-08-30 .
  5. ^ Эффективный Go: for, «Наконец, в Go нет оператора запятой, а ++ и -- являются операторами, а не выражениями. Таким образом, если вы хотите запустить несколько переменных в for, вам следует использовать параллельное присваивание (хотя это исключает ++ и --)».
  6. ^ Спецификация языка программирования Go: операторы If
  7. ^ "Оператор запятая". MDN Web Docs . 17 января 2020 г. Архивировано из оригинала 12 июля 2014 г. Получено 25 января 2020 г. Вы можете использовать оператор запятая, когда хотите включить несколько выражений в место, требующее одного выражения.
  8. ^ «Perlop — Операторы Perl и приоритет — Браузер Perldoc».
  9. ^ "2.4. Грамматическая нотация". Oracle Corporation . Архивировано из оригинала 22 июля 2019 г. Получено 25 июля 2019 г.
  10. ^ «Запятая (,) — это оператор или разделитель в Java?».

Библиография

Внешние ссылки