stringtranslate.com

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

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

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

Синтаксис

Оператор запятая разделяет выражения (которые имеют значение) аналогично тому, как точка с запятой завершает операторы, а последовательности выражений заключаются в круглые скобки аналогично тому, как последовательности операторов заключаются в фигурные скобки: [1] (a, b, c) — это последовательность выражений, разделенных через запятую, результатом которого является последнее выражение c, while {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 ; интервал я знак равно ( а , б ); /** * Присваивает значение a элементу i. * Эквивалентно: int i = a; интервал б; * Запятые служат разделителями в обеих строках. * Фигурные скобки во второй строке позволяют избежать повторного объявления переменных в том же блоке, * что может привести к ошибке компиляции. * Второй объявленный b не имеет начального значения. * Результаты: a=1, b=2, c=3, i=1 */ int a = 1 , b = 2 , c = 3 ; { int я знак равно а , б ; }                    /** * Увеличивает значение a на 2, затем присваивает значение результирующей операции a + b в i. * Запятые действуют как разделители в первой строке и как оператор во второй строке. * Результаты: a=3, b=2, c=3, i=5 */ int a = 1 , b = 2 , c = 3 ; int я знак равно ( а += 2 , а + б ); /** * Увеличивает значение 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 ; интервал я ; я знак равно а += 2 , а + б ;                       /** * Присваивает значение a элементу i. * Запятые служат разделителями в обеих строках. * Фигурные скобки во второй строке позволяют избежать повторного объявления переменных в том же блоке, * что может привести к ошибке компиляции. * Вторым объявленным b и c не присваивается начальное значение. * Результаты: a=1, b=2, c=3, i=1 */ int a = 1 , b = 2 , c = 3 ; { int я знак равно а , б , c ; }          /** * Запятые действуют как разделители в первой строке и как оператор во второй строке. * Присваивает значение c значению i, отбрасывая неиспользуемые значения a и b. * Результаты: a=1, b=2, c=3, i=3 */ int a = 1 , b = 2 , c = 3 ; int я знак равно ( а , б , с );        /** * Возвращает 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». * В этой строке в качестве операторов выступают запятые. */ возврат ( 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):

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

Макросы

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

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

#include <stdio.h> #include <assert.h>  int main ( void ) { int я ; for ( я = 0 ; я <= 9 ; я ++ ) { утверждения ( ( «я слишком большой!» , я <= 4 ) ); printf ( "i = %i \n " , я ); } вернуть 0 ; }                        

Выход:

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

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

Состояние

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

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

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

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

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

if ( ошибка ) return ( errno = EINVAL , -1 );      

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

если ( ошибка ) { errno = EINVAL ; вернуть -1 ; }       

Избегайте блокировки

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

если ( Икс == 1 ) у знак равно 2 , z знак равно 3 ;         
если ( Икс == 1 ) у знак равно 2 , z знак равно 3 ;         

вместо:

если ( Икс == 1 ) { y знак равно 2 ; г = 3 ;}         
если ( Икс == 1 ) { y знак равно 2 ; г = 3 ; }          

Другие языки

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

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

Рекомендации

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

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

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