Jump to content

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

В 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]

См. также

[ редактировать ]
  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, вам следует использовать параллельное присваивание (хотя это исключает ++ и --)».
  6. ^ Спецификация языка программирования Go: операторы if
  7. ^ «Оператор запятая» . Веб-документы MDN . 17 января 2020 года. Архивировано из оригинала 12 июля 2014 года . Проверено 25 января 2020 г. Вы можете использовать оператор запятая, если хотите включить несколько выражений в место, где требуется одно выражение.
  8. ^ «Perlop — Операторы Perl и приоритет — Браузер Perldoc» .
  9. ^ «2.4. Грамматическая запись» . Корпорация Оракл . Архивировано из оригинала 22 июля 2019 года . Проверено 25 июля 2019 г.
  10. ^ «Является ли в Java запятая (,) оператором или разделителем?» .

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

[ редактировать ]
  • Рамаджаран, В. (1994), Компьютерное программирование на языке C , Нью-Дели: Прентис Холл, Индия.
  • Диксит, Дж. Б. (2005), Основы компьютеров и программирования на C , Нью-Дели: публикации Лакшми.
  • Керниган, Брайан В.; Ричи, Деннис М. (1988), Язык программирования C (2-е изд.), Энглвуд Клиффс, Нью-Джерси: Прентис Холл
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: bb83bb72e911c8c34f430f673a5f6ca9__1692836400
URL1:https://arc.ask3.ru/arc/aa/bb/a9/bb83bb72e911c8c34f430f673a5f6ca9.html
Заголовок, (Title) документа по адресу, URL1:
Comma operator - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)