Оператор запятая
В 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)]
).
Примеры
[ редактировать ]В этом примере различное поведение второй и третьей строк связано с тем, что оператор запятой имеет более низкий приоритет, чем оператор присваивания. Последний пример также отличается тем, что возвращаемое выражение должно быть полностью вычислено, прежде чем функция сможет вернуться.
/**
* Commas act as separators in this line, not as an operator.
* Results: a=1, b=2, c=3, i=0
*/
int a=1, b=2, c=3, i=0;
/**
* Assigns value of b into i.
* Commas act as separators in the first line and as an operator in the second line.
* Results: a=1, b=2, c=3, i=2
*/
int a=1, b=2, c=3;
int i = (a, b);
/**
* Assigns value of a into i.
* Equivalent to: int i = a; int b;
* Commas act as separators in both lines.
* The braces on the second line avoid variable redeclaration in the same block,
* which would cause a compilation error.
* The second b declared is given no initial value.
* Results: a=1, b=2, c=3, i=1
*/
int a=1, b=2, c=3;
{ int i = a, b; }
/**
* Increases value of a by 2, then assigns value of resulting operation a + b into i.
* Commas act as separators in the first line and as an operator in the second line.
* Results: a=3, b=2, c=3, i=5
*/
int a=1, b=2, c=3;
int i = (a += 2, a + b);
/**
* Increases value of a by 2, then stores value of a to i, and discards unused
* values of resulting operation a + b.
* Equivalent to: (i = (a += 2)), a + b;
* Commas act as separators in the first line and as an operator in the third line.
* Results: a=3, b=2, c=3, i=3
*/
int a=1, b=2, c=3;
int i;
i = a += 2, a + b;
/**
* Assigns value of a into i.
* Commas act as separators in both lines.
* The braces on the second line avoid variable redeclaration in the same block,
* which would cause a compilation error.
* The second b and c declared are given no initial value.
* Results: a=1, b=2, c=3, i=1
*/
int a=1, b=2, c=3;
{ int i = a, b, c; }
/**
* Commas act as separators in the first line and as an operator in the second line.
* Assigns value of c into i, discarding the unused a and b values.
* Results: a=1, b=2, c=3, i=3
*/
int a=1, b=2, c=3;
int i = (a, b, c);
/**
* Returns 6, not 4, since comma operator sequence points following the keyword
* return are considered a single expression evaluating to rvalue of final
* subexpression c=6.
* Commas act as operators in this line.
*/
return a=4, b=5, c=6;
/**
* Returns 3, not 1, for same reason as previous example.
* Commas act as operators in this line.
*/
return 1, 2, 3;
/**
* Returns 3, not 1, still for same reason as above. This example works as it does
* because return is a keyword, not a function call. Even though compilers will
* allow for the construct return(value), the parentheses are only relative to "value"
* and have no special effect on the return keyword.
* Return simply gets an expression and here the expression is "(1), 2, 3".
* Commas act as operators in this line.
*/
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):
++p, ++q;
++p; ++q;
Макросы
[ редактировать ]Запятую можно использовать в макросах препроцессора для выполнения нескольких операций в пространстве одного синтаксического выражения.
Одним из распространенных вариантов использования является предоставление пользовательских сообщений об ошибках в неудачных утверждениях. Это делается путем передачи списка выражений в скобках в метод assert
макрос, где первое выражение — это строка ошибки, а второе выражение — это утверждаемое условие. assert
макрос дословно выводит свой аргумент в случае сбоя утверждения. Ниже приведен пример:
#include <stdio.h>
#include <assert.h>
int main ( void )
{
int i;
for (i=0; i<=9; i++)
{
assert( ( "i is too big!", i <= 4 ) );
printf("i = %i\n", i);
}
return 0;
}
Выход:
i = 0 i = 1 i = 2 i = 3 i = 4 assert: assert.c:6: test_assert: Assertion `( "i is too big!", i <= 4 )' failed. Aborted
Однако макрос Assert обычно отключен в рабочем коде, поэтому используйте его только в целях отладки.
Состояние
[ редактировать ]Запятую можно использовать внутри условия (if, while, do while или for), чтобы разрешить вспомогательные вычисления, в частности вызов функции и использование результата с областью действия блока :
if (y = f(x), y > x) {
... // statements involving x and y
}
Похожая идиома существует в Go , где синтаксис оператора if явно допускает использование необязательного оператора. [6]
Комплексный возврат
[ редактировать ]Запятую можно использовать в операторах возврата для присвоения глобальной переменной или выходного параметра (передаваемого по ссылке). Эта идиома предполагает, что присваивания являются частью возврата, а не вспомогательными присваиваниями в блоке, который завершается фактическим возвратом. Например, при установке глобального номера ошибки:
if (failure)
return (errno = EINVAL, -1);
Более подробно это можно записать так:
if (failure) {
errno = EINVAL;
return -1;
}
Избегайте блокировки
[ редактировать ]Для краткости можно использовать запятую, чтобы избежать блока и связанных с ним фигурных скобок, например:
if (x == 1) y = 2, z = 3;
if (x == 1)
y = 2, z = 3;
вместо:
if (x == 1) {y = 2; z = 3;}
if (x == 1) {
y = 2; z = 3;
}
Другие языки
[ редактировать ]В языках программирования OCaml и Ruby для этой цели используется точка с запятой («;»). JavaScript [7] и Перл [8] используйте оператор запятая так же, как это делает C/C++. В Java запятая — это разделитель, используемый для разделения элементов списка в различных контекстах. [9] Это не оператор и он не возвращает результат к последнему элементу в списке. [10]
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ «Оператор запятая» . Microsoft Документация для разработчиков . Архивировано из оригинала 2 августа 2019 года . Проверено 1 августа 2019 г.
Два выражения, разделенные запятой, оцениваются слева направо. Левый операнд всегда оценивается, и все побочные эффекты выполняются до вычисления правого операнда.
- ^ «P1161R2: Устарело использование оператора запятой в выражениях индексации» . www.open-std.org . Проверено 05 сентября 2022 г.
- ^ Марк Хёммен; Дейзи Холлман; Корантен Жабо; Изабелла Муэрте; Кристиан Тротт (14 сентября 2021 г.). «Многомерный индексный оператор» (PDF) .
- ^ «СФИНАЭ» . ru.cppreference.com . Проверено 30 августа 2022 г.
- ^ Эффективный ход : для , «Наконец, в Go нет оператора-запятой, а ++ и -- являются операторами, а не выражениями. Таким образом, если вы хотите запустить несколько переменных в for, вам следует использовать параллельное присваивание (хотя это исключает ++ и --)».
- ^ Спецификация языка программирования Go: операторы if
- ^ «Оператор запятая» . Веб-документы MDN . 17 января 2020 года. Архивировано из оригинала 12 июля 2014 года . Проверено 25 января 2020 г.
Вы можете использовать оператор запятая, если хотите включить несколько выражений в место, где требуется одно выражение.
- ^ «Perlop — Операторы Perl и приоритет — Браузер Perldoc» .
- ^ «2.4. Грамматическая запись» . Корпорация Оракл . Архивировано из оригинала 22 июля 2019 года . Проверено 25 июля 2019 г.
- ^ «Является ли в Java запятая (,) оператором или разделителем?» .
Библиография
[ редактировать ]- Рамаджаран, В. (1994), Компьютерное программирование на языке C , Нью-Дели: Прентис Холл, Индия.
- Диксит, Дж. Б. (2005), Основы компьютеров и программирования на C , Нью-Дели: публикации Лакшми.
- Керниган, Брайан В.; Ричи, Деннис М. (1988), Язык программирования C (2-е изд.), Энглвуд Клиффс, Нью-Джерси: Прентис Холл
Внешние ссылки
[ редактировать ]- « Эффект использования запятой вместо точки с запятой в C и C++ », Stack Overflow на русском
- Boost.Assignment — средство из библиотеки Boost , позволяющее легко заполнять контейнеры путем перегрузки оператора запятой.